全局环境

前端结构

模块化

Zero UI中处理模块弃用了相对路径模式,整个导入过程直接使用模块导入的方式,类似:

import Ux from 'ux';

一来方便做重构、二来方便做整体迁移,模块定义位于项目中的 config/modules.js 中,您可以从源代码中看到目前规划的模块所有信息,模块主要如下:

  1. 内部模块:做Zero UI和Zero Extension框架研发才会使用的模块,必须是按调用层次执行导入(下层模块不可导入上层模块)。

  2. 外部模块:这部分模块才是开发人员可以使用的模块,如 Ux, Ex, Sk, Ui 等。

Zero UI中的完整模块参考下表(只有内部模块按排序执行,外部模块不遵循文件名排序规则):

模块名 类型 路径 含义

app

i config t 自定义模块

src/app

应用级自定义模块专用存放目录。

mock

i config t 自定义模块

src/app@mock

纯前端运行的Mock模拟数据专用目录。

plugin

i config t 自定义模块

src/app@plugin

应用级插件专用存放目录。

entity

m mix 外部模块

src/entity@em

Zero 数据模型外部专用模块。

web

m mix 外部模块

src/economy

(标准组件)Zero UI组件外部专用模块。

oi

m mix 外部模块

src/extension/eclat

(配置组件)Zero UI配置化专用组件目录。

ei

m mix 外部模块

src/extension/ecosystem

(扩展组件)Zero UI扩展组件专用目录。

ex

m mix 外部模块

src/extension/library

扩展工具库

ui

m mix 外部模块

src/ui

快速开发库

ux

m mix 外部模块

src/ux

标准工具库

environment

m mix 内部模块

src/environment

环境专用模块,用于存储当前系统环境中的全局配置信息。

lang

m mix 内部模块

src/cab

资源目录专用模块。

zei

i config 内部模块

src/unfold

内部扩展呈现库,Zero Extension Interface。

zep

i config 内部模块

src/upper

内部扩展处理库,Zero Extension Processor。

zet

i config 内部模块

src/utter

内部扩展工具库,Zero Extension Toolkit。

zero

i config 内部模块

src/zero

对接层(复杂组件),Zero(Aeon Interface)。

zs

i config 内部模块

src/zest@web

子系统层(交互式组件),Zero Subsystem。

zi

i config 内部模块

src/zion

交互式组件接口抽象层,Zero Interface。

zmr

i config 内部模块

src/zither@em

查询模型层,Zero Model Qr Engine。

zo

i config 内部模块

src/zodiac

组件起源层(从这层开始有组件概念),Zero Origin Component。

zme

i config 内部模块

src/zoe@em

环境模型层,Zero Model for Environment。

zone

i config 内部模块

src/zone

环境区域底座,Zero Zone。

skin

m mix 内部模块

src/skin

皮肤管理器,最底层的多风格样式导入。

style

m mix 内部模块

src/style

样式管理器。

好了,问题来了,整个框架这么多模块,那么作为开发人员应该如何使用呢?上述模块除了 z 前缀的模块遵循上下层的排序(排在上边的调用下边),其他模块不遵循该法则(历史原因),所以整体从上往下的层级关系如下:

ui              # import Ui from 'ui';
ei / oi         # 由于很多时候都采用API模式,所以这两个库一般开发过程中很少用
ex              # import Ex from 'ex';
 zei
 zep
 zet
plugin          # import Plugin from 'plugin';
ux              # import Ux from 'ux';
 zero
entity          # import {xxx} from 'entity';
 zs
web             # import {xxx} from 'web';
 zi
environment     # import {xxx} from 'environment';
 zmr
 zo
 zme
 zone
skin            # import Sk from 'skin';
style           # @import "~style/index";   # SCSS 模式

上述结构就是整体的调用结构,排序从上往下,开发人员只需要关注行内有注释的部分,您可以按表格中的图标来区分,此处提供开发人员使用模块的黄金法则:

模块 更改 使用 含义

i config t

o

o

自定义模块,自由修改。

m mix

x

o

开发人员可直接使用 import 方式导入的模块,不推荐更改。

i config

x

x

开发人员不可以直接 import ,所有的API都归并到 Ux / Ex 中了。

因为历史原因,其实开发人员真正在开发过程中通常只会使用 Ux / Ex 完成所有的任务,所以直接使用这两个库就可以满足 95% 以上的开发需求了,很多时候不需要太多特殊代码,而我们每个目录中的文件除了当前文件夹下边的 import 语句,单个文件不会导入到目录之外的位置,如下:

import React from 'react';
import Sk from "skin";
import __ from "./Cab.module.scss";
import Ux from "ux";
import {ProCard, StatisticCard} from "@ant-design/pro-components";
import Op from './Op'
import {Popover} from "antd"
import reservation from './images/reservation.png';
import hotel from './images/hotel.png';
import order from './images/order.png';
import room from './images/room.png';
import checkin from './images/checkin.png';

从代码片段就可以知道这种方式的结构最大的好处,就是可以将整个目录中的内容完整移动到另外的目录下执行,这样重构就变得特别容易了。

最后说一句,您也可以修改 config/modules.js 扩展自己想要的模块,只要不破坏本章中提到的整体调用结构即可。

应用结构

前文提到了 app 前缀的部分,是开发人员的自定义模块,除开这部分内容以及前边介绍的模块化之外,您的代码目录中应该还剩下:

  • container:模板专用目录

  • components:页面专用目录

二者的完整结构如下:

0

一个前端站点的页面主要包含两部分:模板页和内容页,模板页位于 container 中,内容页则位于 components 中,最终形成的结构如上图所示。内容页和模板的关联关系有两种定义方法:

定义方式 含义

全局路由表

直接在 src/route.json 文件中定义,内容如:

{
    "defined": "_module_page",
    "special": {
        "_login_index": [
            "_login_index"
        ]
    }
}

这种方式是最早的方式,整体设计的确很方便区分不同页面对应的模板,但过于集中化,这种模式只需要在 Cab.json 中配置 ns 即可。

页面路由表

直接在 Cab.json 中定义 tpl 属性,内容如下:

{
    "ns": "components/login/index",
    "tpl": "_login_index"
}

这种方式是新模式,缺点是除开默认页面以外,可能其他所有页面都需要定义 tpl 才可以被识别。

开发入门

多语言环境下的Cab

Cab多语言环境的支持是Zero UI中的一个亮点,您可以在Zero UI按自己需求随时扩展不同语言级的模块实现多语言环境,语言处理步骤如下:

  1. 在您的环境变量文件: .env.development.env.production 中配置 Z_LANGUAGE 环境变量,如 cn

  2. cab 模块中读取对应目录下的内容,如 cn 则读取资源文件的根目录走 cab/cn

  3. 解析模块对应的 Cab.json 中的名空间( ns ),名空间决定了资源文件的目录路径。

  4. 根据最终 @Ux.zero 绑定的文件名( cab("xxx") )来决定读取哪个资源文件。

在Zero UI中开发时,若您想要实现 多语言 模式,则禁止在您的页面代码中出现任何常量显示文本,如下边这种写法:

<div>你的位置</div>

而是直接使用资源文件绑定来实现,将所有呈现文字放到资源文件中,若您想要切换到另外的语言版本,则可以直接翻译之后替换同名文件包,如将 cab/cn 切换到 cab/en,最终在 Z_LANGUAGE 中设置 en 的变量。

资源关联拓扑

Zero UI中的资源引用拓扑图如下(同时包含模板页和内容页):

0

从图上可以看到,模板的绑定和页面的绑定是分别执行,而绑定过程中还会受到 Z_LANGUAGEZ_ROUTE 环境变量的影响:

  • Z_LANGUAGE:控制当前应用读取的资源包的语言。

  • Z_ROUTE:控制当前应用的 一级路径 (通常表示应用)。

固定入口文件是 UI.js ,若您创建了新的页面,需要重新启动容器(执行 run-zero.sh / run-zero.bat)才能生效,核心链接文件参考下表:

链接类型 路径

内容页链接

src/components/index.js

模板页链接

src/container/index.js

扩展页链接

src/extension/components/index.js

  • 扩展页链接是OOB专用链接页面,可以被内容页链接重写,如您若创建了 src/components/rbac/user/UI.js ,则 src/extension/components/rbac/user/UI.js 就会被覆盖,该功能方便您定制扩展框架中的部分不满足需求的内置页,同时可享受后端扩展页面的路由规划表——理论上所有页面都可以被重写。

  • 所有 index.js 的核心链接文件都不提交到 GIT 仓库,防止不同的人修改造成版本冲突,运行脚本启动时会自动生成(自动化编程)。

  • 通常开发步骤是直接创建所有页面所需的 UI.js,然后一次性生成所有页面,编排到路由表中。

  • Zero UI中的链接文件只支持二级,格式如:src/components/<module>/<page>/UI.js,此处包括所属模块和所在页面。

示例:代码演示

若要实现上述资源引用部分的代码,您只需要按如下步骤进行:

假设环境变量:

  • Z_LANGUAGE = cn

  • Z_ROUTE = nm

假设您想要的页面为订单处理页:

  • <module> = order

  • <page> = process

  1. cab/cn/ 中创建您的资源文件:components/order/process/UI.json

    名空间资源绑定文件并没有严格要求一定要按照页面层级关系定义,但是推荐和您的 components 下的 <module>/<page> 保持一致以防止混乱,方便维护。

  2. src/components 中创建您的目录以及入口文件:order/process/UI.js,创建之后重启容器:run-zero.bat / run-zero.sh

  3. 在您的代码目录中创建 Cab.json 的名空间文件,并指向名空间目录:

    {
        "ns": "components/order/process"
    }
  4. 在您的 UI.js 中追加如下代码:

    import Ux from 'ux';
    // 注解修饰
    @Ux.zero(Ux.rxEtat(require("./Cab"))
        // 此处的 UI 证明它绑定的文件名为 UI.json
        .cab("UI")
        .to()
    )
    // 类定义
    class Component extends React.PureComponent{
        // 核心渲染方法
        render(){
            // 读取 UI.json 中的信息
            const info = Ux.inHoc(this, "info");
            const info1 = Ux.fromHoc(this, "info");
            return ...
        }
    }
    // 组件导出
    export default Component

上述代码示例中 inHoc / fromHoc 是两个一模一样的方法,inHoc 是新版,而旧版是 fromHoc,您可以直接从资源文件中提取数据,上述内容提取了 info,那么资源文件如下:

{
    "_info": {

    }
}

重点:此处的资源文件所有根节点都必须带 _ 前缀,不带此前缀的会被框架自动忽略,而调用API时则不需要使用前缀信息,此处含有配置分区、分块、分类的基础规范

常用组件

日志器

日志器在 Zero Extension 会将部分 彩色日志 打印在浏览器的开发工具 console 中,常使用的 骨架代码 如:

    return Ex.yoRender(this, () => {
            // ....
    }, Ex.parserOfColor("PxRBACGroup").page());

您可以调用 Ex.parserOfColor 方法构造 日志器生成器 对象,之后调用对应的方法实现日志器的选择,上述骨架代码中使用了 page 类型的日志器。

可用日志器 使用场景

internal

内部自定义

private

私有组件

form

表单组件

list

列表组件

action

列表四个区域专用

tpl

模板页

component

公有组件

container

容器专用,对应 src/container

page

页面组件,对应 src/components

type

带有 :type 类型参数的维度页面

control

自定义组件

dynamic

动态配置页

view

视图组件(仅查看,包括报表页)

define

用户自定义页面,对应 src/app

toolkit

(开发中心)工具开发专用

normalize

(开发中心)标准化专用

上述 日志器 并不存在严格规定,只是一种约定,根据目前开发过的所有项目的一个不成文的软文,您也可以根据自身情况选择不同的 日志器 来辅助开发 或自定义更多日志器帮助开发人员监控开发过程中的所有日志。

Assist 辅助数据

Assist 在整个 Zero Extension 中已经成为了一种概念:辅助数据,又或者称为关联数据,一般用来描述主要管理模块中与之相关的其他表中的数据,让主模型的属性变成可管理型的模块数据。常用的辅助数据加载配置如下:

  • _assist:这种节点通常位于当前某个页面的 UI.json 配置文件中,一旦使用 @Ux.zero 加载过资源文件之后可直接在 componentDidMount 中调用对应的API加载,最终更新到状态中。

  • xxx.assist:这种节点一般位于组件内部,如 _form.assist 属于表单内部的辅助数据。

Zero Extension中有三种常见的辅助数据( Assist / Datum 语义):

辅助数据类型 含义

TABULAR

列表类辅助数据,主要访问 X_TABULAR(本章开发)形成不带 树型结构 的辅助数据。

CATEGORY

分类辅助数据,主要访问 X_CATEGORY 形成带 树型结构 的辅助数据。

ASSIST

带有Ajax配置和定义的辅助数据,可以访问任何一张系统中的表,通常依赖您开放出来的接口,此接口不可通用,所以用户可定制。

定义和消费

Assist 数据的基本规范如下

  1. 所有的字典都会有一个名字,在 Zero 框架中使用单词 DATUM 来描述,这个名字通常格式是 :prefix.name

  2. 此处 prefix 表示当前字典数据的前缀,而 name 则是字典本身的相关信息,一般同一类型的字典拥有相同的 prefix,需注意的是当前页面定义的 辅助数据 的名称必须是唯一的。

  3. 在消费绑定时候,字典的名称会直接使用 source=prefix.name 的方式进行 辅助数据 绑定以及消费。

  4. 辅助数据和字典数据在 React 组件中一般会存在于 props 或 state 中,每个字典的命名规则使用 $a_prefix_name$t_prefix_name,为了开发人员简化处理,所有 API 中的传入参数依旧使用 prefix.name(您不用去关心组件中变量使用的是什么,仅需要在调试时知道如何识别即可)。

参考如下定义:

{
    "_assist": {
        "ajax.groups": {
            "uri": "/api/group/by/sigma"
        },
        "ajax.groups.type": {
            "uri": "/api/type/tabulars/:type",
            "magic": {
                "type": "FIX:zero.group.type"
            }
        }
    }
}

从定义可以知道此处的 Assist 辅助数据的数据结构和 Ajax 定义很近似,都包含了 uri, method 等和远程通信相关的基础配置,包括参数中支持 输入解析器 的配置格式。上述示例中定义的字典如:

字典名字 消费配置 含义

ajax.groups

source=ajax.groups,value=key,label=name

从远程 RBAC 模块中的读取 S_GROUP 表中的数据构造组列表信息。

ajax.groups.type

source=ajax.groups.type,value=code,label=name

从远程全局字典配置中读取 X_TABULAR 中的 组类型 中的

加载 Assist 辅助数据的骨架代码如:

    // 标准模块的书写方法(带企业信息的加载)
    componentDidMount() {
        Ex.yiStandard(this).then(Ux.pipe(this));
    }


    // 纯 Assist 数据加载
    componentDidMount() {
        Ex.yiAssist(this).then(Ux.ready).then(Ux.pipe(this))
    }

注意此处的组件本身是经过了 @Ux.zero 的资源文件绑定过的,若是纯组件不具备加载 Assist 的条件(Form除外,新版表单和流程拥有内置的 assist 驱动流程)。

关于继承

Assist 辅助数据还有一种定义方式如:

{
    "_assist": {
        "user.departments": {
            "uri": "/api/dept/by/sigma",
            "inherit": "resource.departments"
        },
        "user.teams": {
            "uri": "/api/team/by/sigma",
            "inherit": "resource.teams"
        },
        "ajax.groups": {
            "uri": "/api/group/by/sigma",
            "inherit": true
        }
    }
}

上述辅助数据定义中,采用了 继承,数据继承的结构图如下:

0

上述结构图中可以知道,一般 父组件 的辅助数据来自两个方向:

  • 自身的 props:一般辅助数据来自父组件

  • 自身的 state:一般负数数据来自自身组件的加载

而这些数据会在往子组件传递时合并到子组件的 props 中,继承 功能点如下:父组件将字典 dict.name1 传入子组件中时,若子组件中出现了同名字典定义 dict.name2,此时这个字典就可以开启 继承 功能,继承有两个值:

  • true:子组件中的辅助数据和父组件辅助数据开启同名继承。

  • String:子组件中的辅助数据为父组件中的辅助数据开启别名模式(数据本身从父组件继承而来)

继承最大的好处是防止 组件嵌套 层次比较深时,同名字典出现了多次从远程加载的情况,正常模式下字典本身在一个 页面 级维持一份是最好的安排,但往往实际开发过程中不会这么简单。

批量配置

前边所有示例都是单字典模式,为了减少前后端交互,针对 TABULAR / CATEGORY 这两种字典类型前端提供了快速配置通道,使用此配置可以帮助 开发人员 执行批量配置,但这种配置模式仅针对 TABULAR / CATEGORY 有效。参考下边配置:

{
    "_assist": {
        "tabular": {
            "uri": "/api/types/tabulars",
            "method": "POST",
            "magic": {
                "$body": [
                    "norm.law.type",
                    "norm.law.policy"
                ]
            },
            "group": "type"
        }
    }
}

_assist 节点的子节点下边有两个特殊节点:

节点名 含义

tabular

列表类型的字典配置,只访问 X_TABULAR 表。

category

树型的字典配置,只访问 X_CATEGORY 表。

此处您可以关注的配置是 group,此处的 group 表示您读取出来的所有字典是按什么属性(此处的 type)进行分组,分组之后,这样一个配置会往后端发送一次 Ajax 远程请求,但 type 有多少种最终就生成多少字典,简单说上述配置在底层会生成两个字典,底层等价于:

-- 实际执行的SQL
SELECT * FROM X_TABULAR WHERE `TYPE` IN ('norm.law.type', 'norm.law.policy');

-- 但是上边配置在前端会对应如下设置
-- 字典一:norm.law.type
SELECT * FROM X_TABULAR WHERE `TYPE` = 'norm.law.type'     -- 等价SQL,实际不执行
-- 字典二:norm.law.policy
SELECT * FROM X_TABULAR WHERE `TYPE` = 'norm.law.policy'   -- 等价SQL,实际不执行

简单说最终数据集会按照 type 进行分组,每组一个字典,而字典名称就是 type 的值。

synonym 同义语义

动态建模中,由于模型本身已经携带了属性别名,加上 UI_LIST / UI_FORM 可以作为模型专属来对待,一般不使用 synonym 语义(虽然也支持)。往往在动态建模流程过程中,表单和列表的属性名在自身配置中可定义,而属性名本身就具备两层语义:模型层 / 展示层,加上内置了映射组件保驾护航,根本不用担心第二形态的存在。这种模式下直接采用传统方式的 拷贝(此时的拷贝是低成本的),就可以直接完成大量的新模型、表单、列表模块的开发,所以此时 synonym 的语义显得比较 鸡肋

synonym 同义语义一般在常用框架中都不会存在,开发人员完全可以使用 拷贝/复制 大法直接在 OOB 的基础上做一个新的模块,但是这样的代价是维护的成本比较高。synonym 语义处理的是 业务多态 的场景(目前版本主要做 呈现层 标签重命名):

exp form synonym

语义 的优势在于应付 需求变更。它的诞生原因:

  1. OOB 标准化模块一直处于开发和变动状态,如果使用传统的 直接拷贝 的方式,整个模块的版本会变得不容易维护,而每次开发完成之后您必须使用 拷贝 的方式将变更部分更新到新系统中,这样一旦出现大的基础模块的变动,您的整个系统升级会变得复杂——也可以理解 synonym 是一种面向升级的语义。

    这种设计和 Zero Ui 中采取最初的 分发器 模式改成如今的一个单独的自动化指令:ai sync 是如出一辙,虽然这个命令目前也是使用的 拷贝,但 拷贝 这个动作是机器托管,自动分析,自动计算,最终类似自动化更新程序来更新框架,且更新部分禁止开发人员改动。

  2. 部分扩展的力度并没有达到要底层的表结构和模型发生变动,这种场景下拷贝完整的表、实体、模型无疑是一种 杀鸡用牛刀 的玩法,为了避免这种玩法,synonym 语义可以让您单纯从 业务语义 上穿一件外衣,而不去改动底层(接口可以使用统一授权模式,也可使用分离授权模式)。典型场景如:员工管理扩展出供应商员工管理、驻场员工管理、合作伙伴员工管理、内部员工管理,在目前的 OOB 模式下,基础的 E_EMPLOYEE 一直都岿然不动,只是单纯通过拓展的方式来实现:

    • 若没有任何新属性的需求,仅需将部分 展示层 重命名,如 员工工号 → 驻场员工编号,这种级别的变更直接用 synonym 同义语义是最快的。

    • 若出现了新的属性的需求,那么可以采取 父主表父从表 两种连接模式也可以达到新增属性的需求,但这种模式下,OOB 标准模块中的属性依旧依赖 synonym 同义语义来装饰。

所以,同义语义既可以保证 已运行模块 的升级、变更、扩展流程,又可以兼容新模块的开发,还支持部分已存在的模块直接 积累/沉淀 成标准化模块,可谓一举三得。

表单配置

同义语义的表单配置位于 _form 节点之下:

{
    "_form": {
        "synonym": {
            "name": "退款单标题",
            "amount": "退款金额"
        }
    }
}

同义的表格示例(下边配置中不再解释)

属性 OOB标准化模块 同义后标签

name

单据标题

退款单标题

amount

单据金额

退款金额

由于在目前版本的收款单、退款单两个核心对象中很多属性都是重复的,仅金额的正负不同,此时这两种模型出现了 同质化设计,于是 同义 语义就十分有作用,几乎“零成本”的方式就可以改造出两个新的基于标准化模块继承过来新模块(只是底层共享了表结构)。

列表配置

同义语义的列表配置位于 _grid 节点之下(参考 Ex.yiListSynonym 用法):

{
    "_grid": {
        "synonym": {
            "title": "法规标题",
            "description": "法规文档备注"
        }
    }
}
流程表单配置

同义语义的流程表单配置位于工作流定义的 UI_CONFIG 部分,通常如下:

{
    "synonym": {
        "phase": "销毁单状态",
        "title": "销毁单标题",
        "serial": "销毁单号"
    }
}
编程配置

编程过程中处理起来就更加简单,直接搜索 Zero Ui 中的 $synonym 关键字就可以看到多数组件支持的同义语义部分,此处就不赘述,在后续实战章节注意拆解和讲解。

远程通信

Ajax基础

Zero UI中的Ajax调用一般可以直接使用 Ux.xxx 的API,基本使用如下:

import Ux from 'ux';

// 最终发送请求:GET /app/name/vie.app.zui
Ux.ajaxGet("/app/name/:name", {name:"vie.app.zui"})

上述代码中直接调用了 Ux.ajaxGet 方法发送 GET 请求,所有的Ajax类的API主要包含三个大类:

  • 请求类:安全模式 / 非安全模式标准请求。

  • 配置类:纯异步回调模式(古老的callback方式)。

  • 响应类:和 Zero UI结合的各种不同响应模式相关。

请求类

请求类 API 主要设计和考虑维度如下:

  • 是否执行Spring中带有服务名称(serviceName)的微服务模型的API调用。

  • 是否执行安全请求:

    在 Zero 框架基础规范中,默认 / 类型的API是公开类型的API,而带有 /api 前缀的API为标准API(即需安全认证的API),若客户端执行 安全请求,脚本会根据当前应用的认证模式(Basic, OAuth, Digest)为客户端请求生成 Authorization 头相关信息;不仅如此 Zero Framework 还支持 数字签名 功能,若同时在客户端和服务端启用了数字签名功能,签名模块 会自动为每个请求计算 sig 参数,默认使用算法 HMAC-512(算法可同时在前后端配置)。

  • 上传/下载类的请求都是基于 安全模式,其中分为 GET/POST 两种不同的API。

  • 请求类的签名统一为 (uri, params, options)(service, uri, params, options),而返回值统一为 Promise

参考表格看看 请求类 API的详细信息:

HTTP方法 安全模式 标准 微服务 含义

GET

ajaxGet

microGet

标准GET请求

GET

ajaxDownload

GET下载

GET

ajaxFetch

microFetch

公开GET请求

GET

ajaxResource

当前站点资源读取专用API,一般读取当前站点的HTML页做动态加载专用。

POST

ajaxPost

microPost

标准POST请求

POST

ajaxUpload

标准上传请求

POST

ajaxPull

POST下载

POST

ajaxPush

microPush

公开POST请求

PUT

ajaxPut

microPut

标准PUT请求

DELETE

ajaxDelete

microDelete

标准DELETE请求

上述的API的常用参数表如下:

参数名 类型 含义

service

String

「微服务」专用参数,用于表示服务名。

uri

String

请求专用路径,可使用类似 :param 的路径参数发送请求。

params

Object

请求参数,通常是 key=value 的键值对。

options

Object

请求头设置,可设置不同请求头(默认会计算部分请求头)。

配置类

配置类API会根据配置的方法执行分流操作(之前叫做 回调类,升级后语义有变化),通常配置类API都以 async 做方法前缀,现阶段所有配置类的异步API包含如下:

方法 含义

asyncTrue

该方法通常用于 检查 函数,一般是检查数据存在或丢失专用的一个Ajax接口,这种接口只返回 true / false,函数签名如 (config, params, callback)

asyncData

该方法用于执行标准远程请求,远程请求正常则直接执行 callback 回调,这种接口自由点,可返回所有满足要求的异步回调,函数签名如 (config, params, callback, mockData)

asyncPromise

该方法在上述提到的标准函数上执行了 配置封装,可支持Ajax的配置模式驱动远程通信请求。

asyncImage

该方法为图片加载专用方法,图片源可以是某个站点内图片,也可以是远程的二进制流转换的图片(手动 MIME)。

asyncWrap

该方法为封装专用方法,在原始的Ajax配置类接口中封装了一层,封装类在部分地方起到了防御式效果,所以此方法属于新版正在使用的方法。

AJAX的标准配置如下(根据HTTP方法执行不同配置):

const V_ASYNC_FN = {
    get: __AJX.ajaxGet,
    post: __AJX.ajaxPost,
    put: __AJX.ajaxPut,
    fetch: __AJX.ajaxFetch,
    push: __AJX.ajaxPush,
    delete: __AJX.ajaxDelete
};

配置类API的两个核心参数此处有所讲究:

  • config 参数:

    此参数通常格式如:

    {
        "uri": "xxx",
        "method": "xxx",
        "params.criteria": {
        },
        "magic": {
        }
    }

    配置参数中必须说明的是两种不同的取参模式:

    • 直接使用 params 的方式提取参数,这种模式提取参数只支持 criteria 部分的 输入解析

    • 使用 magic 的方式提取参数,这种模式会直接对参数执行 输入解析,且将解析结果注入到 QR 查询参数部分。

  • callback 参数:

    callback作为回调配置,支持两种不同的格式:

    • Function 格式,直接使用编程的方式传入单结果回调,只传入 success 时的回调。

    • Object 格式,这种格式会包含 success / failure 两种回调,成功和失败分别走不同的回调。

响应类

响应类API主要用于后期处理,后期和前端界面交互渲染以简化回调效果信息,比如:

  1. 提交成功后,显示成功消息( message 类)。

  2. 提交成功后,弹出对话框提示结果( Modal 类)。

  3. 若提交失败,上述两种结果(红色)以失败的方式返回。

响应类API和其他类型不同,部分响应类API包含了 二阶 模式,二阶模式一般为 配置方式 专用,二阶的 编程方式 代码通常如下:

// 2阶使用如
const $opConfirm = (todo = {}, ref) => (reference) => (data = {}) => {
    const request = {key: todo.key, data};
    return Ex.I.todo(request, true)
        // 二阶API弹出对话框提示
        .then(Ux.ajax2Dialog(ref, buildConfig(ref, "confirmed"), true))
        .then(response => Ex.rx(reference).close(response));
};

响应类API的清单如下

1阶函数 2阶函数 含义

ajaxError

x

异常回调,解析错误信息专用,异常发生之后会执行 防重复提交 的表单还原。

ajaxDialog

ajax2Dialog

正常回调,使用弹出框显示正常执行之后的回调效果。

ajaxMessage

ajax2Message

消息模式,使用 message 消息组件执行回调效果,一般是显示一条大约 1.628 秒的基本消息。

x

ajax2True

特殊二阶回调,返回值为 true / false,通常做 存在检查丢失检查

messageSuccess

打印成功消息。

messageFailure

打印失败消息。

messageCatch

打印严重错误的基本消息。

最后结合源代码再讲一下回调配置,通常回调配置片段如下:

"_modal": {
    "error": {
        "empty": "对不起,请在权限组中添加权限信息!"
    },
    "success": {
        "added": "恭喜,您已经成功添加了一条权限到当前权限集,您可以继续添加权限或关闭当前窗口!",
        "saved": "恭喜,您已经成功更新了所选择的权限信息!"
    }
}

上述片段中的配置通常位于 UI.json 的顶层,您可以直接调用 Ux.ajaxDialog 直接解析或提取配置,它有三个 根值

根键 含义

error

红色异常信息,通常是错误信息,调 Ux.ajaxError 可生效。

success

绿色成功信息,调 Ux.ajaxDialog 可生效。

confirm

提示框( Yes/No ),同样调 Ux.ajaxDialog 可生效,且 Zero UI 会自动配置窗口函数。

参数详解

前文讲解了前端专用的Ajax远程通信API,本章针对这些API的参数部分详细讲解,如此,开发人员就了解参数的 解析 / 配置 流程了。

uri

所有远程通信的第一个参数几乎都是 uriservice + uri 的格式,而 URI 格式处理过程会执行默认逻辑:

  1. 若存在 :param 的路径参数,那么参数会一式两份,除了路径上的参数会被替换,而 Query 查询参数中也会包含,追加 ?param=xxx

  2. 所有的查询参数在提交过程中都会执行 encoding 的操作执行编码,后端会自动解码,以防止特殊字符、中文、标点的不合法性。

# 调用 Ux.ajaxGet("/api/user/:name", {
#    name: "Lang",
#    email: "silentbalanceyh@126.com
# })
GET /api/user/:name
# 参数params如下
{
    "name": "Lang",
    "email": "silentbalanceyh@126.com"
}

由于上边的 URI 带有 :name 的路径参数,所以此处会直接生成:

GET /api/user/Lang?name=Lang&email=....

有几种情况会直接将参数忽略:

  • 参数本身是 nullundefined 的非法值。

  • 参数长度小余 40 时会被忽略(参考IBM产品),实际过程中最长的路径参数不外乎UUID格式,由于标准 UUID 格式带有中划线所以长度会是 36,所以查询参数有一个 40 长度的限制。

当然您会觉得 40 长度是不是太少了,如果您有一个字符串需要追加在路径参数中,这个参数值最好是不超过 40 的,若超过了这个长度您就可以考虑是否可通过 设计 去规避这个问题,如直接使用查询参数、或直接使用 Body。

默认参数

默认参数为系统配置的专用参数,若您使用了环境变量 Z_LANGUAGE,此参数会自动追加到您的 请求体 中( language=cn ),但请求体会包含两种格式:

  • 直接请求格式

    Ux.ajaxPost("/api/ui/ops",{control: key, type: "FORM"})

    如上边代码中的直接格式会将 params 参数作为 Body 发送到远程,若自动追加就会变成如下 Body 格式:

    {
        "control": "xxx",
        "type": "FORM",
        "language": "cn"
    }
  • 间接请求格式

    间接请求格式一般使用的是路径参数格式,通常会将路径参数和 Body 区分开,如下:

    Ux.ajaxPost(`${options[__Zn.Opt.AJAX_SEARCH_URI]}?QBE=:qbe`, {
        qbe: $qbe,
        $body: query
    });

    上述格式中默认参数会自动追加到 $body 中而不是根数据结构上。

在部分特殊场景下如 QR 参数中,默认参数 不会被追加到请求中,以防止默认参数影响查询条件,虽然从 Zero Framework 中的语言部分可值,此条件不影响最终查询,在某些场景下此处的 language 参数也起到了限定作用。

禁用关键字

禁用关键字是 Zero Framework 独有的,由于 Zero 包含了查询参数 Qr 的数据结构如下:

{
    "criteria":{},
    "pager":{
        "page": 1,
        "size": 20
    },
    "sorter": [
        "createdAt,DESC"
    ],
    "projection": []
}

检查查询参数会以上述四个键为最高优先级,若包含了上述键作属性,Zero UI会将此请求判断成为 查询引擎 请求,部分前端组件会受到影响,所以在实际请求中需规避 criteria, projection, sorter, pager 关键字以防止请求参数混淆。

安全请求

Zero 中的安全请求通常使用 /api/ 路径,主要支持如下功能:

  1. 跨域安全请求

  2. token 的标准安全请求

  3. xsrfToken 的安全请求

  4. 数字签名功能

跨域安全请求直接在环境变量中配置:

Z_CORS_CREDENTIALS=include
Z_CORS_MODE=cors

上述环境变量控制了跨域处理中的选项 options,对应:

{
    "mode": "cors",
    "credentials": "include"
}

标准安全格式主要以计算 Authorization 的值为主,会根据现阶段的 安全模式 执行计算,目前支持 Basic, Digest, OAuth 三种,常用的两种如下表:

模式 token Authorization格式

Basic

xxx

Basic xxx

OAuth

xxx

属性解析器

解析器基础

设计目的

Zero UI中的一个很大的亮点就是 属性解析器,属性解析器也可以称为配置解析器,您可以通过十分简洁的默认代码将属性解析成 Json Object 的方式来标准化配置格式。属性解析器的设计目的如下:

  1. 让开发人员更集中于配置中和业务直接相关的部分而 忽略配置的数据格式

  2. 对 Json 格式的配置进行压缩,有了属性表达式之后原始配置信息会压缩到 20% 的体积,大大减少了开发人员对配置的实施量。

  3. 属性标准化:针对部分特殊属性可执行标准化操作,包括效果、文本、标签、图标等。

Zero UI中的属性解析器主要应用于如下场景(默认启用):

  • 常用的表单字段,配合 AntD 实现模型属性的配置化定制。

  • 常用的列表列渲染,支持各种不同的列渲染结果。

  • 窗口、请求等常用属性解析器,可提高开发人员开发这些定制组件的效率。

解析格式

前端配置的通用解析格式主要有如下三种:

  1. (最简)纯字符串解析格式,如:

        "title,标题,,,,placeholder=格式如:***公司,normalize=text:128"
  2. (高频)开发专用格式,如:

    {
        "metadata": "companyId,所属公司,,,aiTreeSelect,placeholder=(请选择所属公司)",
        "optionJsx.config.datum": "source=resource.companys,value=key,label=name",
        "optionJsx.config.tree": "text=name,parent=companyId",
        "optionJsx.config.selection": "mode=FULL",
        "optionConfig.rules": [
            "required,请选择员工所属的公司!"
        ]
    }

    开发专用格式和其他两种格式的区别在于:

    • 这种格式通常会有专用的 metadata 属性作可解析部分,可解析部分 的格式和最简格式中的字符串格式是一致的。

    • 这种格式支持属性的拉平语法,如示例中的 optionJsx.config.datum 这种键值,Zero UI会将它自动解析。

    • 这种格式除开 metadata 之外,会带上部分附加配置(半Json结构),这些结构一般是为 复杂场景 而量身打造的,所以其作用主要是补充。

  3. (完整)标准格式,如前文中格式展开之后如下:

    {
        "field": "companyId",
        "optionItem": {
            "label": "所属公司"
        },
        "$render": "aiTreeSelect",
        "optionJsx":{
            "placeholder": "(请选择所属公司)",
            "config":{
                "datum": {
                    "source": "resource.companys",
                    "value": "key",
                    "label": "name"
                },
                "tree": {
                    "text": "name",
                    "parent": "companyId"
                },
                "selection":{
                    "mode": "FULL"
                }
            }
        },
        "optionConfig": {
            "rules": [
                {
                    "type": "required",
                    "message": "请选择员工所属的公司!"
                }
            ]
        }
    }

    标准格式在整个配置表中是最规范的,如果使用程序生成对应的格式,推荐使用标准格式;标准格式会跳过属性解析部分,直接将格式中的JSON作为配置来执行处理,这种格式的缺陷是长度比较大(完整格式是最长的),开发人员在书写时略有不方便。

参考表格看看三种不同格式的使用场景,让开发人员更清楚其用法:

格式 场景 说明

最简格式

简易配置

如果您想要书写的配置可直接使用最简格式配置,那么采用最简格式属于最优解。

开发格式

复杂配置

若某些配置中依赖比较复杂的规则,如:复杂组件、验证规则、渲染分流、自定义组件、远程参数、流程配置 等这种复杂场景中往往需要采用此种格式。

标准格式

机器配置

若您使用的是 开发中心 的工具对配置进行生成或标准化,那么采用这种格式是最优,跳过属性解析部分且不需使用人工书写。

拉平语法

拉平语法是 Zero UI 中书写配置的另外一大亮点,其目的也是为了 压缩配置体积,参考如下配置:

{
    "optionJsx":{
        "style":{
            "height":300
        },
        "config":{
            "title":[
                "left",
                "right"
            ]
        }
    }
}

上述配置若是手工书写会写一堆 {}[] 来展开完整配置,阅读比较规范,但体积(行数)偏高,有用的信息只有 1/3 左右,使用了拉平配置后其内容如下:

{
    "optionJsx.style.height": 300,
    "optionJsx.config.title": [
        "left",
        "right"
    ]
}

拉平配置中默认格式可采用不同方式拉平,如上述配置也可以写成(只要符合拉平原理的格式都是这种格式支持的,这点大大提高了开发人员书写配置的自由度):

{
    "optionJsx":{
        "style.height": 300,
        "config.title": [
            "left",
            "right"
        ]
    }
}

表达式

一般属性表达式的格式如:v0,v1,v2,v3,这种格式下,对应的 v0 表示索引为 0 的值,这种场景下会根据不同的 组件,其属性名有所区别,若某个一索引中无值,则需置空,若从某一个属性开始全部为空则可不用考虑,如:

# 如下边表达式 v2, v3 无值,但由于 v4=aiTextArea,所以需占位符
name,姓名,,,aiTextArea

# 下边格式 v2 开始全部为空
title,标题

若在使用过程中遇到了扩展属性,则要启用 $KV$ 占位符,$KV$ 占位符用于填充 原子解析器 中的原子属性,通常使用 key=value 的格式来执行解析,此处的 key=value 在后续的 原子解析器 章节中会有所说明。 :data-uri: :table-caption!:

原子解析器($KV$

表单/列表 部分解析器属于上层解析器,放到表单和列表章节中去处理,本章先从不同的视角讲讲特殊的几大类解析器。

原子属性表

原子解析器是Zero UI中针对特殊配置节点的 字符串 格式的解析器,主要用于书写字符串和 metadata 属性值,通常原子解析器格式如:attributeName=(value) 的格式。

原子属性并没有使用字典序的方式排列,相反是按使用场景排列的:表单、列表、按钮、全局。

原子属性
属性名 配置节点 场景 含义

normalize

optionConfig.normalize

表单

此属性用于设置 输入限制 的不同模式,当前框架中支持不同的输入限制,解决的问题背景可以参考如下:

- 多格式的表达式问题:https://gitee.com/silentbalanceyh/scaffold-zero/issues/I6W2BB

incribe

optionJsx.incribe

表单

当某个字段处于只读状态时(readOnly=true),为了让呈现文字不显示成灰色禁用状态,可直接设置 incribe 属性显示黑色明显的只读文字。此属性比较特殊,在 HTML 的底层控件中有两种:<input/>, <select/>,这两种不同的控件在设置只读时的模式有所区别:

  • <input/> 可直接设置 readOnly 属性完成只读的模式。

  • <select/> 则必须设置 disabled 属性才能完成只读模式。

不论哪种模式造成的后果就是在只读状态下文字会变成 灰色,这样会导致部分只读的 阅读障碍,为了解决此问题才开启 incribe 属性来做文字呈现。

moment

(当前)

表单

针对时间控件如 <DatePicker/> 必须设置此属性防止 undefined 的时间格式出现,不论是旧库 moment.js 还是 dayJs 都不支持 undefined 的非法值,若您的表单字段是时间格式的操作控件而您没有设置此属性 moment=true,那么时间格式在转换过程中会报错。

addonAfter

optionJsx.addonAfter

表单

后置插件,针对 addonAfter 属性进行设置,支持 图标表达式

addonBefore

optionJsx.addonBefore

表单

前置插件,针对 addonBefore 属性进行设置,支持 图标表达式

prefix

optionJsx.prefix

表单

前置文字,针对 prefix 属性进行设置,支持 图标表达式

suffix

optionJsx.suffix

表单

后置文字,针对 suffix 属性进行设置,支持 图标表达式

placeholder

optionJsx.placeholder

表单

输入之前的水印提示文字。

format

optionJsx.format

表单

针对时间、日期控件专用的格式化 pattern

valuePropName

optionConfig.valuePropName

表单

此属性主要针对 <CheckBox/><Radio/> 两种组件,新版默认使用了 checked 作为值绑定,基本不用配置。

withCredentials

optionJsx.withCredentials

表单

「上传」组件中设置跨域模式必须的属性。

text

optionJsx.text

表单

「上传」组件显示文字自定义。

listType

optionJsx.listType

表单

「上传」设置组件种类,原生 AntD 支持的值:text, picture, picture-card, picture-circle,在 Zero UI中只实现了 text, picture-card 两种。

allowClear

optionJsx.allowClear

表单

除了常用的录入型组件、下拉组件之外,大部分自定义组件也支持此属性,不同点在于开启此属性之后部分自定义组件会多一个 清空 按钮。

rows

optionJsx.rows

表单

此属性是多行文本专用属性,主要针对 <Input.TextArea/> 标签的属性,可直接设置文本跨越的行信息。

maxLength

optionJsx.maxLength

表单

该属性是长度限制的原始属性,一般 Zero UI中使用 normalize 代替,主要原因如下:

  • 此属性计算的是字符集,针对中文或双字符文集无法准确计算长度,有可能导致计算不准。

  • 通常在文本做长区域限制的时候会使用此属性代替 normalize 属性(长区域一般很少会输入满中文的情况)。

min

optionJsx.min

表单

此属性是 <InputNumber/> 组件专用属性,用于设置值下限。

max

optionJsx.max

表单

此属性是 <InputNumber/> 组件专用属性,用于设置值上限。

precison

optionJsx.precision

表单

此属性是 <InputNumber/> 组件专用属性,设置当前数值的精度(小数点之后的位数)。

step

optionJsx.step

表单

此属性是 <InputNumber/> 组件的步进数,设置数值的每次变化的数值。

type

optionJsx.type

表单

此属性用于组件中对组件本身执行 类型 识别专用。

showTime

optionJsx.showTime

表单

时间控件中时间格式专用属性,是否显示时间部分信息。

mode

optionJsx.mode

表单

控件中的模式:

  • 时间控件支持值包括:time、date、month、year、decade

  • <Select/> 下拉多选时支持:multiple、tags

maxTagCount

optionJsx.maxTagCount

表单

多选专用属性,当多选时使用 tags 模式时表示最多可支持的多选标签数量。

autoFocus

optionJsx.autoFocus

表单

自动聚焦属性,默认会将焦点聚焦到此控件。

showSearch

optionJsx.showSearch

表单

是否显示搜索框,此属性主要针对 <Input.Search/> 组件的搜索功能效果呈现。

readOnly

optionJsx.readOnly

表单

(状态)当前组件为 只读 时的专用属性。

disabled

optionJsx.disabled

表单

(状态)当前组件为 禁用 时的专用属性。

status

optionItem.status

表单

针对表单中的 <Form.Item/> 状态专用属性。

itemClass

optionItem.className

表单

此属性用于设置表单中 <Form.Item/> 标签的 className 属性。

colon

optionItem.colon

表单

此属性用于设置表单中 <Form.Item/> 标签的 colon 属性,是否显示双引号。

labelSpac

optionItem.labelCol

表单

影响当前组件布局的专用属性,对应 labelCol

wrapperSpan

optionItem.wrapperCol

表单

影响当前组件布局的专用属性,对应 wrapperCol

fixed

(当前)

列表

此属性主要针对表格控件中的 <Column/> 列进行设置,支持的值为:left, right,将列固定到左侧或右侧。

size

(当前)

按钮

此属性最早是针对 <Button/> 进行设置,实际只要支持 size 属性的配置都生效,AntD 中支持的值如:large、middle、small

shape

(当前)

按钮

此属性最早是针对 <Button/> 进行设置,解析按钮中的 shape 属性,AntD 中支持的值如:default、circle、round

_submit

(当前)

按钮

「旧版」这个属性属于旧版本提交按钮专用属性,可设置提交按钮的提交相关内容,最终提交属性会触发 Form 相关的提交行为,生成按钮对应的提交行为配置。

closable

optionJsx.closable

按钮

新版用于配置按钮关闭行为的专用属性。

callback

optionJsx.callback

按钮

新版用于配置按钮回调行为的专用属性。

api

optionJsx.api

按钮

最新版用于直接配置远程 API 而无需额外的编程配置处理 A, S, D 链接。

sorter

(当前)

 — 

Ajax远程查询引擎排序参数 sorter,用于指定排序专用属性信息。

group

(当前)

 — 

分组专用属性,只要支持 group 属性的组件都可被解析,且在 Zero UI中的提交表单中针对提交按钮开启 group 属性,打开 <Button.Group/> 功能。

key

(当前)

 — 

主键属性,若某个组件没有 key 值,则使用此属性配置来完善 key 值,这是 React 规范所需的内容。

className

(当前)

 — 

风格专用属性,设置 JSX 中专用属性 className(等价于 CSS 中的 class 属性)。

特殊场景

输入限制:normalize

输入限制是比较常用的一个属性,您可以直接使用 normalize 设置,现阶段 Zero UI 中支持的输入限制如下:

含义 格式 示例

decimal

只录入浮点数

<值>:<长度>:<精度>

normalize=decimal:12:2

number

只录入数值

<值>:<位数>

normalize=number:10

id

只录入账号和标识

<值>:<长度>

normalize=id:16

integer

只录入大于`0`的数

<值>:<位数>

normalize=integer:4

length

录入不超过长度的数值。

<值>:<长度>

normalize=length:16

text

录入不超过长度的文本(中文算1的长度)。

<值>:<长度>

normalize=text:512

upper

录入不超过长度的大写。

<值>:<长度>

normalize=upper:32

实际场景示例如下:

{
    "metadata": "name,银行名称,16,,,placeholder=请输入名称,normalize=text:40",
    "optionConfig.rules": [
        "required,请输入名称,名称不可为空!"
    ]
}
排序参数:sorter

排序参数一般在Ajax格式中使用(压缩版),用来处理查询引擎专用排序参数,参考如下示例:

{
    "ajax": {
        "metadata": "POST,/api/customer/search,1,10,sorter=name`ASC",
        "params.criteria": {
            "sigma": "PROP:app.sigma",
            "type": "FIX:corporation",
            "": "OPERATOR:AND"
        }
    }
}

上边配置中的 metadata 片段会直接被解析,而专用参数 sorter 的格式写法如下:

  1. 针对单个字段的排序

    # 按 name 升序排列
    sorter=name`ASC
  2. 针对多个字段的排序

    # 先按 name 升序排列、再按 code 降序排列。
    sorter=name`ASC;code`DESC
  3. 上述设置的解析结果(全格式)为:

    [
        "name,ASC"
    ]

    [
        "name,ASC",
        "code,DESC"
    ]
输入水印变换

水印相关的属性如下:

  • incribe

  • placeholder

这两个属性主要用于切换 只读 效果切换,它们支持一个特殊值 $CLEAR$,这个值会清除掉已设置好的水印文字,主要在 只读/禁用 两种状态切换时,三态表单中会有如下状态:

  • 可编辑:水印文字需要继续显示在交互式组件中。

  • 只读:若不显示水印文字(通常禁用),则需设置 $CLEAR$ 的效果,但若依旧要显示文字,则可能依赖 incribe 启动文字的呈现。

图标表达式

上述表格中还提到了图标表达式,它属于一个比较特殊的值(主要是 suffixprefix),用于设置属性关联的图标信息(`<Icon/>`专用转换),最终会换成如下:

/*
 * 直接将 suffix / prefix 的字符串转换成对象格式,此格式会注入内部实现 `<Icon/>` 的设置
 * 此处的 type 表示 `<Icon type="xxxx"/>`,而新版会自动解析成所需的图标类型格式。
 **/
optionJsx.suffix = {
    type: value
}

组件解析器

本章节讲解 组件解析器,组件解析器位于 原子解析器 之上,它的解析通常根据场景有所区别,不同场景下 索引 对应的属性名不同。

窗口解析器

Zero UI中的窗口解析器主要分如下三种:

  1. 弹出窗口,对应 AntD 中的 <Modal/> 组件。

  2. 抽屉窗口,对应 AntD 中的 <Drawer/> 组件。

  3. 浮游窗口,对应 AntD 中的 <Popover/> 组件。

窗口连接

Zero UI中的窗口连接是一种比较 怪异 的模式,这种模式拥有历史原因:

H5中现阶段已经支持表单内 <form/> 的不同方式的提交按钮 <button/>、<input/>,同时也可以直接支持表单外的组件来提交(Segment模式),但Zero UI最早开发的时候,表单外的提交模式还没成型和落地,所以使用了比较原生态的一种 协变 做法来处理窗口连接。

它的原理图如下:

0

  1. 窗口外和窗口内部表单会形成一个父子级结构,二者使用了不同的 React 组件来完成组装。

  2. 外窗口有两个核心变量(位于 state 中):

    • $visible:控制窗口的显示和隐藏,若触发函数 rxClose 则会直接关闭窗口,将此状态值设置成 false

    • $submitting:控制窗口外层按钮的 防重复提交,点击提交按钮之后不论是 Ok 还是 Cancel 在窗口级都会处于加载状态。

  3. 提交按钮主要分两层:

    • 内层为表单内渲染的提交按钮(display = none),该提交按钮带 id 属性(如 id=$opSave),此按钮虽然对用户不可见,但属于 被连接点

    • 外层为窗口中的渲染按钮,一般是确认,属于 连接触发点,它内置会直接调用 Ux.connectId("$opSave") 执行代码触发内置按钮的点击事件。

    • 上述两个按钮之间的交互就是本章的 窗口连接

  4. 最后 rxClose 函数作为窗口关闭函数会从父组件(窗口)传入到子组件(表单)中,当表单提交完成后,可直接触发关闭,此时还会重设 防重复提交 状态。

弹出窗口

弹出窗口属性的定义如下:

    [
        "title",
        "okText",
        "cancelText",
        "visible",
        "width",
        "maskClosable",
        "onOk",
        "component"
    ]

消费示例如下:

{
    "_window": "配置信息,选择,关闭,false,800,true,$opSaveApi"
}

上述消费的配置最终会解析成如下:

{
    "title": "配置信息",
    "okText": "选择",
    "cancelText": "关闭",
    "visible": false,
    "width": 800,
    "maskClosable": true,
    "onOk": () => Ux.connectId("$opSaveApi"),
    "component": null
}

上述配置中的基础定义遵循 AntD 组件中的 <Modal/> 属性信息,详细属性可参考官方文档进行解读。由于 AntD 新版本使用了 open 代替 visible 属性,为了保证整个框架向前兼容型,所以此处依旧使用 visible 属性,Zero UI框架会自动执行转换。此处的 visible 属性需要如下解读:

  • 上述 visible 只是一个初始状态,通常弹出框的默认状态是 visible = false 的,但有会有类似 通知板 的情况(默认就是打开状态);实际执行过程中 visible 通常会和窗口组件的 $visible 状态值绑定到一起,并结合 rxClose 的关闭函数实现完整的窗口闭环。

  • onOk 在此处配置的是连接点的 id,前文已经讲过 窗口连接 相关知识点,此处的 onOk 最终会解析成一个 触发函数

弹出窗口完整的属性表如下:

索引 属性 含义

0

title

当前窗口的标题显示文字,一般由外层配置。

1

okText

确认按钮显示的文字,若没有配置则省略 确认按钮,某些场景下弹出窗口不依赖提交,直接提供关闭按钮即可。

2

cancelText

取消按钮文字,若只有单独按钮可以考虑修正成 关闭

3

visible

窗口默认状态是显示还是隐藏。

4

width

窗口的宽度,宽度值一般是以 px 像素为单位,实际执行过程中也可以使用 xx% 的格式,该值会注入 CSS 的属性中去。

5

maskClosable

点击遮罩效果时是否允许关闭,默认一般点击遮罩不允许关闭(直接使用 $visible 控制),若设置成 true 则点击时可关闭窗口。

6

onOk

连接点的按钮 id,通常设置成 <Button id="xxx"/>,此处的 xxx 则是 id 值。

7

component

默认 null,一般子元素会在编程过程中直接传入,就不会启用 component 属性了,若从 编程模式 切换到 配置模式,此属性就是必须的,表示内置子组件的组件名称。

编程过程中您可以使用下边代码执行 纯解析 流程:

import Ux from 'ux';

// expression格式:
// 1. 字符串格式:配置信息,选择,关闭,false,800,true,$opSaveApi
// 2. 标准格式:{}
const config = Ux.aiExprWindow(expression)
抽屉窗口

抽屉窗口的属性定义如下:

    [
        "title",
        "placement",
        "width",
        "closable",
        "maskClosable",
        "visible"
    ]

消费示例如下:

{
    "window.extra.view": "视图管理,right,400,true,false",
}

上述消费的配置最终会解析成如下:

{
    "title": "视图管理",
    "placement": "right",
    "width": 400,
    "closable": true,
    "maskClosable": false,
    "visible": false
}

窗口配置 有区别的点在于抽屉窗口配置没有 确认 按钮,只包含了关闭按钮;其中还有一点在于 抽屉窗口 不存在窗口连接的功能,其内部不用调用 Ux.connectId 的API来同步两个不同按钮的状态。

抽屉窗口完整的属性表如下:

索引 属性 含义

0

title

窗口标题。

1

placement

抽屉方向,主要包含`top, bottom, left, right`四个值。

2

width

抽屉窗口的宽度,一般`left, right`使用,如果`top, bottom`则该值表示高度。

3

closable

是否支持`关闭`功能。

4

maskClosable

点击遮罩是否允许关闭。

5

visible

该窗口默认显示值。

编程过程中您可以使用下边代码执行 纯解析 流程:

import Ux from 'ux';

// expression格式:
// 1. 字符串格式:视图管理,right,400,true,false
// 2. 标准格式:{}
const config = Ux.aiExprDrawer(expression);
浮游窗口

浮游窗口的属性定义如下:

    [
        "title",
        "placement",
        "width",
        "closable",
        "visible"
    ]

消费示例如下:

{
    "window.extra.column": "请选择您要显示的列,leftTop,640,true"
}

最终解析结果如下:

{
    "title": "请选择您要显示的列",
    "placement": "leftTop",
    "width": 640,
    "closable": true,
    "visible": false
}

浮游窗口的完整属性表如下:

索引 属性 含义

0

title

窗口标题。

1

placement

浮游方向,有八个:leftTop, left, leftBottom, bottom, rightBottom, right, rightTop, top

2

width

浮游窗口的宽度。

3

closable

是否支持`关闭`功能。

5

visible

该窗口默认显示值。

编程过程中您可以使用下边代码执行 纯解析 流程:

import Ux from 'ux';

// expression格式:
// 1. 字符串格式:请选择您要显示的列,leftTop,640,true
// 2. 标准格式:{}
const config = Ux.aiExprPopover(expression);

Ajax解析器

Ajax解析器是为了针对后端特殊场景执行请求核心解析,其中包括:

  1. QR 查询引擎语法专用解析

  2. 参数解析(Qr参数、普通参数、Magic参数)

  3. 字典 / 分类 专用解析器

Ajax的属性定义如下:

    [
        "method",
        "uri",
        "params.pager.page",
        "params.pager.size",
        "$KV$"
    ]

该解析主要是针对 QR 查询引擎语法,配置部分写入如下:

{
    "metadata": "POST,/api/user/search,1,10,sorter=updatedAt`DESC",
}

上述配置被解析之后会生成如下完整配置部分:

{
    "method": "POST",
    "uri": "/api/user/search",
    "params":{
        "pager": {
            "page": 1,
            "size": 10
        },
        "sorter": [
            "updatedAt,DESC"
        ]
    }
}

Ajax的完整属性如下:

索引 属性 含义

0

method

Ajax使用的HTTP方法。

1

uri

Ajax调用的远程URI路径。

2

params.pager.page

分页功能中的页码,从`1`开始。

3

params.pager.size

分页功能中的每页记录数,默认`10`。

4

$KV$

专用键值对处理。

字段解析器(表单)

表单字段解析器属于 高频 解析器,且 $KV$ 中的内容比较丰富,参考 原子解析器 查看 表单 类的属性信息,表单解析器的属性定义如下:

    [
        "field",
        "optionItem.label",
        "span",
        "optionJsx.style.width",
        "render",
        "$KV$"
    ]

在某些复杂场景中,通常会使用 metadata 来定义解析表达式(如添加验证规则、添加窗口配置等),表单解析器在此处就不举例了(源代码中这部分内容比较多),提供一个表单字段解析的示例:

{
    "metadata": "companyId,所属公司,,,aiTreeSelect,placeholder=(请选择所属公司)",
    "optionJsx.config.datum": "source=resource.companys,value=key,label=name",
    "optionJsx.config.tree": "text=name,parent=companyId",
    "optionJsx.config.selection": "mode=FULL",
    "optionConfig.rules": [
        "required,请选择员工所属的公司!"
    ]
}

表单字段的完整属性如下:

索引 属性 含义

0

field

表单对应字段名。

1

optionItem.label

表单字段前边的标题。

2

span

当前表单字段在Grid布局中宽度,最大宽度为24(善用 AntD 中的 Grid 布局),默认根据列的不同可设置。

3

optionJsx.style.width

当前表单的相对宽度,一般为百分比。

4

$render

该表单调用的渲染API,用于渲染不同组件专用。

5

$KV$

专用键值对处理。

此处的顺序比较讲究,在某些大表单模式下,通常一个属性只需要使用 name,名称 这种最简化的格式即可(此时 span 可根据标准布局计算,所以后续内容都可以不用设置)。

列解析器(列表)

列解析一般用于列表定义(使用了 AntD 中的 <Table/> 标签),此处可支持各种不同的列渲染,列解析器的属性定义如下:

    [
        "dataIndex",
        "title",
        "$render",
        "sorter",
        "$KV$"
    ]

列解析器的消费示例如下:

    "columns": [
        "name,权限名称",
        "code,权限编码"
    ]

此处解析的属性结果就不枚举了,列的完整属性如下:

索引 属性 含义

0

dataIndex

绑定的记录对应的属性名。

1

title

当前列的列标题。

2

$render

列的渲染类型,该类型在列表章节详细解析。

3

sorter

是否打开列排序,如果打开则列中会启用排序功能。

4

$KV$

专用键值对处理。

按钮解析器

按钮解析理论上只应该只有一种,由于 历史原因,此解析器如今已经拓展成如下四种:

  • 连接按钮

  • 提交按钮

  • 组件按钮

  • 命令按钮(特殊面板)

连接按钮

前文提到了 窗口连接 的基础概念,连接触发按钮 会单纯调用 Ux.connectId 去触发另外一个按钮点击事件。

连接按钮的属性定义如下:

    [
        "key",
        "text",
        "connectId",
        "type",
        "icon",
        "disabledKey",
        "$KV$"
    ]

一般这种按钮会位于外层组件中,如:

  1. Tab 页签的 tabBarExtraContent 中按钮连接内容(children)中的隐藏按钮,旧版使用 <TabPanel/>,新版则直接使用 items 属性代替。

  2. Card 左右顶部按钮连接内部(children)中的隐藏按钮。

连接按钮由于需要 连接点连接触发 两处的按钮双向状态同步,所以此处还需要同步 防重复提交,点击之后处于 loading 的状态,以下是 Card 中的部分片段:

    "_page": {
        "title": "新建申请",
        "left": [
            "btnOrderSave,提交,$opCreate,primary",
            "btnOrderReset,重置,$opReset,default"
        ]
    }

上述按钮的最终解析结果如下:

{
    "key": "btnOrderSave",
    "text": "提交",
    "connectId": "$opCreate",
    "type": "primary",
    "icon": null,
    "disabledKey": null,
    "$KV$": null
}

连接按钮的完整属性如下:

索引 属性 含义

0

key

当前按钮的`key`值(React专用)。

1

text

当前按钮显示的文字信息。

2

connectId

被连接的隐藏按钮的id。

3

type

该按钮的类型,对应Ant Design中的`<Button/>`对应的`type`属性。

4

icon

该按钮显示的图标信息,对应`icon`属性。

5

disabledKey

该配置为一个遗留配置,用来控制按钮在什么场景中被动态禁用。

6

$KV$

专用键值对处理。

提交按钮

提交按钮通常位于表单配置中,属于表单 内部专用 按钮,它和 AntD 直接产生绑定作用,实现表单的快速提交,简化开发人员开发提交函数。

提交按钮的属性定义如下:

    [
        "key",
        "text",
        "event",
        "type",
        "className",
        "icon",
        "$KV$"
    ]

其中最核心的配置是 event,它既可用于 编程模式 也可用于 配置模式,通常表单中的提交按钮配置如下:

[
    {
        "metadata": "$button",
        "hidden": true,
        "optionJsx.extension": [
            "$opAdd,添加,SUBMIT,primary",
            "$opReset,重置,RESET"
        ]
    }
]

上述片段中以 $opAdd 为例,解析的最终结果如:

{
    "key": "$opAdd",
    "text": "添加",
    "event": "SUBMIT",
    "type": "primary",
    "className": null,
    "icon": null,
    "$KV$": null
}

提交按钮的完整属性如下:

索引 属性 含义

0

key

当前按钮的`key`值(React专用),表单中绑定触发函数也是依赖该key做函数名。

1

text

当前按钮显示的文字信息。

2

event

当前表单实现的标准化事件的名称,如`SUBMIT`或`RESET`等。

3

type

该按钮的类型,对应Ant Design中的`<Button/>`对应的`type`属性。

4

className

Zero中定义了不同颜色和风格的按钮专用className,该配置主要用于风格切换。

5

icon

该按钮显示的图标信息,对应`icon`属性。

6

$KV$

专用键值对处理。

组件按钮

组件按钮是Zero UI中的自定义组件 <DialogMenu/> / <DialogButton/> 专用,这两种组件可实现 按钮/菜单 内嵌子组件模式,点击模式下的弹框或窗口,和窗口配置配合之后,实现很复杂的交互模式。

组件按钮的属性定义如下:

    [
        "key",
        "text",
        "type",
        "icon",
        "confirm",
        "$KV$"
    ]

组件按钮的消费配置如下:

{
    "button": "itemCancel,撤销,,undo,若执行撤销,则这单据全部会变成无效,确认?,className=ux-spec"
}

上述配置最终会解析成:

{
    "key": "itemCancel",
    "text": "撤销",
    "type": null,   // 此处会使用 default 做默认值
    "icon": "undo",
    "confirm": "若执行撤销,则这单据全部会变成无效,确认?",
    "$KV$": "className=ux-spec"
}

组件按钮的完整属性如下:

索引 属性 含义

0

key

当前按钮的`key`值(React专用),表单中绑定触发函数也是依赖该key做函数名。

1

text

当前按钮显示的文字信息。

2

type

该按钮的类型,对应Ant Design中的`<Button/>`对应的`type`属性。

3

icon

该按钮显示的图标信息,对应`icon`属性。

4

confirm

该按钮是否启用确认/取消的提示浮游窗口功能,打开选项后设置成浮游提示文字。

5

$KV$

专用键值对处理。

上述配置中需要针对 confirm 进行特殊说明,这个属性会影响最终的 JSX 输出,通常是使用在 删除 按钮中。

// 不带 confirm 的操作
<Button onClick={fnEvent}/>

// 带 confirm 的操作
<Popconfirm title={confirm} onConfirm={fnEvent}>
    <Button/>
</Popconfirm>
命令按钮

命令按钮主要在 G6 的绘图板中使用,属于 专用按钮,这种按钮也可以扩展到其他场景,这种按钮的特殊点在于其内部触发模式不遵循 React 的基础原理,而是使用 H5 的模式在触发。

    [
        "key",              // 事件专用 key,依靠这个绑定
        "text",             // 显示文字
        "className",        // 风格处理
        "confirm",          // confirm 窗口
        "confirmPosition",  // confirm 位置
        "icon",             // 图标信息
        "tooltip",          // tooltip 打开(打开过后文字放到 tooltip中)
        "$KV$"
    ]

命令按钮解析配置属于 高级用法,主要操作为 Ux 中三个核心API:

  • opExtra,右上角附加命令按钮渲染专用。

  • opLink,链接命令按钮专用。

  • opCommand,标准命令按钮专用。

命令按钮不用绑定 onClick 函数,全部是外层传入内层触发,如下边配置

"_commands": [
    "$opAutoSave,,ux-spec,确认打开?,left,undo"
]

上述按钮最终会解析成:

{
    "key": "$opAutoSave",
    "text": null,
    "className": "ux-spec",
    "confirm": "确认打开?",
    "confirmPosition": "left",
    "icon": "undo",
    "tooltip": null,
    "$KV$": null
}

命令按钮的完整属性如下:

索引 属性 含义

0

key

当前按钮的`key`值(React专用),表单中绑定触发函数也是依赖该key做函数名。

1

text

当前按钮显示的文字信息。

2

className

Zero中定义了不同颜色和风格的按钮专用className,该配置主要用于风格切换。

3

confirm

该按钮是否启用确认/取消的提示浮游窗口功能,打开选项后设置成浮游提示文字。

4

confirmPosition

浮游窗口的位置,该配置仅存在于命令模式的按钮。

5

icon

该按钮显示的图标信息,对应`icon`属性。

6

tooltip

该配置用于命令模式只配置图标型按钮(不显示文字),tooltip为鼠标移动到按钮上的提示文字。

7

$KV$

专用键值对处理。

项解析

项解析存在于各种 复杂组件 内部,主要用于简化小规模的组件开发,如:

  1. 图标完整风格解析 <Icon/>,带色彩、大小、种类。

  2. 数据源过滤条件解析,自动过滤功能。

  3. 下拉、多选、单选项解析

图标解析

图标写法在 Zero UI 中可以简化,由于 历史原因 出现过几次比较大的变动,这些配置主要用于小图标修饰,标准配置定义如下:

    [
        "text",
        "icon",
        "iconStyle.fontSize",
        "iconStyle.color",
        "style.color",
        "$KV$"
    ]

上述内容对应的源代码如:

<span>
    <Icon/>
    &nbsp;&nbsp;
    {文字部分}
</span>

消费示例如下:

    "$mapping": {
        "Pending": "未结算,exclamation-circle,16,#0a7bed",
        "Finished": "已结算,check-circle,16,#268941",
        "InValid": "无效,stop,16,#e22015"
    }

上述示例最终会解析成如下配置:

{
    "text": "未结算",
    "icon": "exclamation-circle",
    "iconStyle": {
        "fontSize": 16,
        "color": "#0a7bed"
    },
    "style":{
        "color": null
    }
}

图标解析的完整属性如下:

索引 属性 含义

0

text

标签显示的文字。

1

icon

图标类型,对应`<Icon/>`中的`type`属性。

2

iconStyle.fontSize

控制图标大小。

3

iconStyle.color

控制图标颜色。

4

style.color

控制文字颜色。

5

$KV$

专用键值对处理。

过滤条件

Zero UI中的组件有 字典关联 模式,这种模式通常会在页面初始化时从服务端远程读取字典所有信息作为核心数据源,再结合字典配置(DATUM)来实现下拉的值和呈现文字的对应效果,数据源配置过程中的基础操作条件如:

  • 纯静态配置,如上边提到的 $mapping 节点,在JSON中直接堆选项相关信息。

  • 动态数据源,使用 DATUM 模式的配置配置远程数据字典

而过滤条件就是为数据源过滤量身打造,如一个下拉完整数据源中包含了 30 个选项,您可以配置一个默认的过滤器,筛选部分选项出来构造此下拉。

数据源配合下拉的属性定义如下:

    [
        "source",
        "field",
        "type",
        "cond"
    ]

上述配置信息的消费片段如:

/*
 * source = form 表示条件值的来源是 form 的 comment 字段,
 * type = integer 则表示整数类型,类型有三
 * - integer:整数
 * - decimal:浮点数
 * - 默认其他类型,如字符串
 * 最终构成的查询条件为:
 * condField = form中的整数字段comment
 **/
form,comment,integer,condField

过滤条件配置比较特殊,不会生成最终解析的JSON格式,而是使用JS中的 Function 替代,它的完整属性表如:

索引 属性 含义

0

source

标识条件字段的值来源,可以是form,可以是state,可以是props。

1

field

标识来源处的某个字段名。

2

type

对于整数和浮点型必须做转换和设置,Zero Ui中所有判断都是`===`的三等号模式。

3

cond

从字典中提取数据时的条件字段,最终形成`cond = value`进行过滤。

选项解析

选项解析主要服务于 单选、多选、下拉 组件,它用来描述每一个固定的项的绑定信息(通常下拉会包含值、文字两部分),选项的属性定义如下:

    [
        "key",
        "label",
        "style"
    ]

上述定义使用的范围比较广泛,也可以手工调用API来实现解析(快速解析项),配置消费段形如:

    "optionJsx.config.items": [
        "STANDARD,标准",
        "VALID,无效"
    ]

上述消费段会被解析成如下结果:

    "items": [
        {
            "key": "STANDARD",
            "label": "标准",
            "value": "STANDARD",
        },
        {
            "key": "VALID",
            "label": "无效",
            "value": "VALID"
        }
    ]

选项解析的完整属性如:

索引 属性 含义

0

key

选项键值,React专用。

1

label

选项呈现的文字信息(支持表达式格式)。

2

style

该选项的风格:文字颜色、大小等(保留,以前用过,现在很少用)。

容器项解析

容器想解析主要针对 <Tab/> 组件,属性定义如:

    [
        "tab",
        "key",
        "icon",
        "$KV$"
    ]

当您使用多个页签来实现页签容器的配置时,则可直接配置对应的页签项而避免复杂配置:

    // 不拉平模式
    "_tabs": {
        "defaultActiveKey": "keyPending",
        "type": "card",
        "items": [
            "等待处理,keyPending",
            "处理完成,keyFinished"
        ]
    }

    // 完全拉平模式(更简洁)
    "_tabs": {
        "defaultActiveKey": "keyPending",
        "type": "card",
        "items": "等待处理,keyPending;处理完成,keyFinished"
    }

最终解析结果如下:

{
    "items": [
        {
            "tab": "等待处理",
            "key": "keyPending"
        },
        {
            "tab": "处理完成",
            "key": "keyFinished"
        }
    ]
}

容器项的完整属性如:

索引 属性 含义

0

tab

页签上显示的文字信息。

1

key

页签的键值,React专用。

2

icon

页签对应的图标。

3

$KV$

专用键值对处理。

输入解析器

输入解析器又称为 数据来源解析器,该解析器的使用场景如下:

  1. 表单初始化字段初始值配置。

  2. 组件的Ajax请求参数配置。

  3. 列表的查询参数设置。

参考如下配置:

{
    "ajax": {
        "metadata": "POST,/api/customer/search,1,10,sorter=name`ASC",
        "params.criteria": {
            "sigma": "x",
            "type": "y",
            "": true
        }
    }
}

上述配置中,此处生成的 伪SQL 条件如下:

sigma = 'x' AND type = 'y'

编程模式 下,此处的 xy 可直接通过脚本制定,但若是配置模式(描述型),此处就要制定不同的数据来源来填充 sigmatype 才能生效,所以才有了 输入解析器 的存在价值,若使用输入解析器,上述配置可能会写成如下:

{
    "ajax": {
        "metadata": "POST,/api/customer/search,1,10,sorter=name`ASC",
        "params.criteria": {
            "sigma": "PROP:app.sigma",
            "type": "FIX:corporation",
            "": "OPERATOR:AND"
        }
    }
}

上述配置表示:

  1. sigma 的值来自于 React Component 中的 props 属性中,从 $app 中提取 sigma 的值。

  2. type 的值为固定值,设置为 corporation 的固定字符串。

  3. 两个条件之间的条件为 AND 连接符。

所以输入解析器通常会使用 <SOURCE>:<EXPRESSION> 的格式执行相关定义,下边章节一一讲解所有的可解析数据源头。

NUMBER

JavaScript 中的数值类型只有 Number,所以数据源中处理数值时需依赖此输入解析器,不可以使用 "0" 的字符串方式表示数值,它的示例片段为:

{
    "person": "NUMBER:1",
    "price": "NUMBER:0"
}

BOOL

布尔值的用法几乎和 NUMBER 一致,直接将参数转换成 Boolean 的数据类型,防止直接使用 "false" 这种字符串字面量方式,示例片段如下:

{
    "active": "BOOL:true"
}

JavaScript 是弱类型语言,通常在执行类型处理时不会直接转换成合法类型来对待,而后端的数据类型是 严格 的,所以很多场景下需要将前端的 JavaScript 中的字符串字面量转换成带有 强类型 语言特征的值才能正确和后端交互,这种场景下 NUMBERBOOL 两种输入解析器的优势就体现出来了。

OPERATOR

此输入解析器只针对 QR 查询引擎生效,用于描述当前层中条件连接符,而根据 Zero 中的查询引擎规范,条件连接符使用空字符串作字段名,所以示例如下:

{
    "params.criteria": {
        "sigma": "PROP:app.sigma",
        "type": "FIX:corporation",
        "": "OPERATOR:AND"
    }
}

注意在 QR 查询语法中,如果不设置连接符,那么默认使用 OR 的连接符来连接查询条件。

FIX

此输入解析器为固定值解析器,后续内容就是设置的值,又称为 常量值解析器,您的配置中写什么就是什么,示例如下:

{
    "value": "FIX:student"
}

上述表达式中 value 的值会被直接解析成 value = student

DELAY

这种格式很特殊( 延迟渲染 ),在 Ant Design 的表单中第一次 render() 方法调用表单时旺旺没有任何初始化操作,这种模式下可能需要启用延迟加载,延迟加载有可能是 componentDidMount 方法中直接从远程读取,也可能是第一次初始化之后从 componentDidUpdate 方法从远程读取,这种类型的延迟操作都依赖这种方式的输入源。

这种模式下延迟字段会引起 Ajax 延迟远程加载后编程数据发送出来。

ENUM

枚举格式 输入解析器,这种格式通常用于多值输入源的情况,示例如下:

{
    "status,i": "ENUM:Requested`Pending`Finished"
}

上述枚举类型最终会解析成如下结构的数据:

{
    "status,i": [
        "Requested",
        "Pending",
        "Finished"
    ]
}

此处注意枚举值之间的格式是使用的 ` 符号,英文逗号 在解析结果中包含特殊的解析含义,因为此原因枚举值和值之间的间隔符使用了此符号。

FORM

特殊输入来源:数值来源于 Ant Design 中的表单,一般初始化过程中不会使用此 输入解析器,初始化过程中由于表单本身可能未加载完成,所以不能使用这种 输入解析器,所以此解析器通常用于表单交互过程,如级联下拉、级联过滤等。示例如下:

{
    "metadata": "floorId,楼层,14,,aiSelect,placeholder=(请选择层信息)",
    "optionJsx.config.datum": "source=floors,key=key,label=name",
    "optionJsx.config.cascade": {
        "source": "tentId",
        "target": "FORM:tentId"
    }
}

上述配置中配置了级联过滤,使用 tentId = 表单选择数据 对当前下拉执行过滤。

DAUTM / UNIQUE

这两种类型的 输入解析器 比较接近,也属于常使用的 输入解析器,它会定义一个字典作为数据源,如一个配置项的状态:

状态字典
显示文字

RUNNING

运行中

PENDING

等待审批

BUILDING

在建

假设此处配置的字典名称为 ci.status(对应到 source 配置),那么本章的 输入解析器 如下:

// 格式一:直接拉取数组的 Array
DATUM:source=ci.status

// 格式二:根据条件对数组执行过滤
DATUM:source=ci.status,code=(xxx)

此处的 xxx嵌套表达式,这种模式只有在特定场景会使用,此处就不累赘了,UNIQUE 输入解析器DATUM 的一种变体,用于读取唯一记录集:

{
    "status": "UNIQUE:ci.status,key,code=FIX:RUNNING"
}

上述结构中消费 ci.status 的内部逻辑如下图:

0

PROP / STATE

此输入源很简单,直接从 React 组件的属性或状态中提取相关值,由于 Zero UI 存在专用数据模型,所以在提取数据时会有一定的规则。示例如下:

{
    "sigma": "PROP:app.sigma"
}

此数据规则对所有类似 xxx.yyy 格式的提取都生效,包括后续会提到的 USERROUTE 等类型。

  • 如果 $app 是一个 DataObject,则直接调用 $app._("sigma") 提取数据。

  • 如果 $app 是一个 JavaScript 中原生的 Object,则直接使用 $app["sigma"] 提取数据。

ROUTE

此输入源直接从 React Router 构造的 $router 变量中提取和路由相关的数据,示例如:

{
    "params": {
         "type": "ROUTE:type"
    }
}

内部会调用 $router._("type") 来提取参数,而路由参数会包含如下两种:

类型 URI格式 含义

路径参数

/xc/customer/:type

浏览器访问 /xc/customer/regular 时,会解析 type = regular

查询参数

/xc/customer/ext?type=test

此时直接解析 type = test

USER

此输入源会从当前登录用户中提取数据信息,切支持 嵌套对象 提取。示例如:

直接提取

{
    "companyId": "USER:companyId"
}

嵌套提取

{
    "companyId": "USER:company.key"
}

上述第二个示例中会从 company = {} 去提取二级嵌套属性的值来执行赋值。

表单配置

表单开发

Zero UI中的表单开发主要以配置为主,而在开发过程中可直接使用模板代码进行开发,开发人员真正需开发的部分:

  1. 开发 Op.js 针对表单中的提交部分执行函数开发。

  2. 开发 UI.js 的入口表单开发。

表单的基础配置以两部分内容为主。

  1. 配置加载:直接从 UI.json 中提取 _form 的配置信息,或从远程访问读取 JSON 部分的数据构造表单基础配置。

  2. 渲染标准化:调用 Ex.yoXxx 系列方法对表单配置执行标准化处理,最终传入 ExForm 组件中执行表单扩展处理。

基础Json

{
    "_form": {
        "layout": "??",
        "className": "??",
        "window": "??",
        "columns": "??",
        "ui": [
            []
        ],
        "assist": {},
        "hidden": [],
        "initial": {},
        "rule": {},
        "op": {},
        "io": {},
        "metadata": {},
        "segment": {},
        "modal": {},
        "rowConfig": {},
        "rowClass": {}
    }
}

上述结构是目前比较常用的一个表单配置的基本结构,也是开发人员必须掌握的完整表单配置结构信息,这份结构基于代码梳理,部分的变化结构并没有在文档中指出,后续可持续更新。

布局属性

Zero UI 中的表单开发使用的是 AntD 中的 <Form/> 标签,它的布局并没有使用官方的 流模式,而使用了经典的 Grid 布局模式(24列),控制表单布局的主要有如下属性:

  • window:选择布局类型,Zero UI中提供的布局类型的值有如下:

    类型 含义

    1

    标准布局

    (默认值可不填写)常用的表单标准布局

    1.1

    增强型标准布局

    对字段的标签文字进行了深度考虑之后更符合复杂表单的核心增强型布局,某些场景下比 window=1 的布局更好用。

    0.15

    9:15布局

    左侧带有很宽页边距的布局模式,一般是 1 列字段的表单布局专用,此处 9:15 表示一个输入字段中的 标签:输入控件 的横向布局信息。

    0.16

    9:15宽文布局

    增强型 0.15 的布局模式,针对某个字段 文字比较多 (如七个字、八个字)的专用布局,一般 0.15 时候标签文字不超过四个的布局方式。

    0.17

    9:15子表单布局

    增强型 0.15 的布局模式,若此表单中包含了 子表单,那么这种布局模式更适合。

    0.21

    Word布局

    这种布局模式属于之前认证评价系统专用布局,表单本身就是一个Word文档,格式也是参考Word文档的格式执行深度定制。

    0.5

    两列标准布局

    常用的 2 列字段的标准布局,通常两列是 12:12 的方式。

    0.51

    两列变种布局

    类似 1 → 1.1 的变体一样,这种类型两列布局可以解决 文字较多 的场景。

    0.101

    10:14布局

    新布局模式,标签:输入控件 横向比例 10:14

    0.121

    12:12布局

    新布局模式,标签:输入控件 横向比例 12:12

    -0.3

    抽屉搜索表单

    右侧抽屉拉出来之后的表单布局模式。

    -0.5

    高级搜索表单

    点击了 高级搜索 之后的布局方式。

    上述布局设置中参考如下注意事项:

    1. 若要扩展新的布局,您可以直接更改 src/zion/variant-layout/ 中新增布局定义文件、扩展新值即可,键使用浮点数。

    2. 一般在表单布局中,负数 表示过滤搜索表单布局,正数 表示提交型表单的布局,大于 1 表示高频布局,小余 1 则表示特殊变体。

    布局本身设计如此复杂也是有 历史原因 的,在早期的 AntD 和新版本的 AntD 中,标签对齐都是直接在编程代码中手工设置,这样的模式会导致大量布局前端的重复代码,且上下字段对齐特别是标签对齐相对麻烦,所以才有了Zero UI中的布局自动计算,布局解决的核心问题是在 字段占用 Grid 布局中的列不同但上下标签依旧是对齐的,这样的解决方式虽然在设置值时会略微复杂,但可以解决超过 300 个字段的大表单的上下布局问题。

  • columns:选择此布局类型中每一行默认容纳的字段数量。

    该值相对比较简单,牵涉到每一列的 span 的自动计算,Zero UI会根据 windowcolumns 两个值自动计算单个字段的 span 属性,让Grid布局更具智能化,某些标准模式下几乎不需要设置 span 就可以完成上下完整对齐。

    含义

    1

    单列表单,每一行只有一个字段,标准模式下:span=24

    2

    双列表单,每一行有两个字段,注 0.5 / 0.51 只支持 columns=2 的情况,标准模式下:span=12

    3

    三列表单,每一行三个字段,标准模式下:span=8

    4

    四列表单,每一行四个字段,标准模式下:span=6

  • layout:对应 AntD 中的属性值,默认是 inline,可支持的值包括:horizontal、vertical、inline

布局除开上述三个属性以外,还有针对单行的处理属性:rowConfig,在 rowConfig 中您可以针对某一个行(索引为键值)的布局执行修正效果,造成模式和模式之前横向的变体,这样其布局就可以突破单个布局的局限性,如 10.15 的混用时,可能依赖调整 rowConfig 来达到 对齐 的效果,若没有此配置那么 window 一旦指定表单就会被固化下来,而在更多复杂场景时 布局固化 是一个十分可怕的事。

关于布局有一个可能困惑开发人员的东西,就是表单渲染中的 key 值,此处的 key 值您可以直接从 Console 的表单渲染器中看到:

exp form layout

此处解释下 key 值的含义,一般 key 值有四位,如截图中的 3008

从左往右
位数 示例 含义

第一位

3

表示当前表单布局的默认列数,只支持 1,2,3,4 四个值。

第二位

0

表示当前属性在这一行的列索引,如 0 表示从左往右的第一个表单属性。

第三、四位

08

表示当前属性占用的宽度,按 Grid 排版,此处使用的值最终为 span=8

上述模式中既可控制到,又可以控制到,有了 行列 配置之后,您可以很容易对当前表单中的组件进行定位,此处解读 key 的目的是为了对应布局文件中的如下内容:

const layout = {
    // ...
    2016: "4,20,98",    // ---- ---- ---- ----
    2108: "8,16",       //                     ---- ----
    2118: "3,21,98",    //        -- ---- ---- ---- ----
    // ...
}

上述布局文件中的左值就是 key,您采用什么 key,那么 label / input 的比值就是多少,而第三位如 98 表示当前组件的宽度百分比,最终会解析 98%。宽度百分比的目的是上下对齐专用。

风格属性

风格属性主要有如下两个属性:

  • className:当前表单的专用属性,默认值 ux_form,若您指定了 className 之后,那么指定的值将会直接追加和覆盖原始的 ux_form(原始风格依旧生效)。

  • rowClass:针对表单单行的属性设置,您可以在一个完整的基于 Grid 的表单中设置某些行特殊的风格信息,如交叉行、反色单行、协变宽高等。

数据处理

数据处理属性主要包含如下属性:

  • assist:辅助数据(字典数据)表单内加载流程。

    表单内若某些字段绑定了字典信息来构造 下拉、多选、单选 等相关信息,这种场景下表单本身依赖字典数据来构造 可操作源,而辅助数据的绑定存在两个不同版本的跨越:

    • 外部源 _assist:此配置位于页面级,不在表单内部,表单内部不用设置任何 assist 的属性,依旧可以使用字典数据绑定。

    • 内部源 assist:此配置位于表单级,在表单内部初始化流程中会被触发,配置格式是一致的。

    外部源的模式比较适合 开发模式,而内部源多用于 配置模式

  • initial:初始化数据

    初始化数据一般用于添加类 mode=ADD 表单,您可以直接在 initial 中根据所需对字段执行添加表单的默认值配置,配置片段如下:

    {
        "initial": {
            "active": "BOOL:true",
            "appId": "PROP:app.key",
            "type": "FIX:ftp",
            "protocol": "ftp",
            "port": "21",
            "path": "/"
        }
    }

    从配置格式可以知道,表单中的初始化数据是支持 输入属性解析 功能的,若您设置的字段值带有 PREFIX: 的前缀,那么此属性会使用 输入属性解析 功能从不同的数据源来填充初始值。

  • rule:延迟初始化

    如果系统中包含了 linker 类型的字段影响连接,那么通常在 Zero UI中使用的是 fieldName / fieldId 的模式进行双字段渲染,然后填充对应渲染的数据,可是会出现一种情况:若此处的 fieldName 是另外一个字段 的数据,并非和 fieldId 绑定的数据那么应该如何操作?此时您可以自定义新的数据加载流程,此加载流程在 rule 中设置,参考如下片段:

    {
        "_form": {
            "rule": {
                "parentName": {
                    "seeking": {
                        "uri": "/api/x-category/:key",
                        "magic": {
                            "module": "fm.subject"
                        }
                    },
                    "linker": {
                        "owner": "owner"
                    }
                }
            }
        }
    }

    简单讲解下上述配置来理解 rule 属性的延迟初始化效果:

    • 上述表单中 parentName 属性名并非对应的是 parentId,此种情况下依赖此处定义的规则在用户读取表单数据时多执行一次数据提取流程。

    • 不仅如此,这种数据规则可访问远程的某个接口将抓取的数据通过 Zero UI中的 linker 机制默认填充各种数据字段,如此处填充的 owner → owner,此处可设置更复杂的延迟初始化规则。

  • io:输入/输出规则(非异步)

    输入/输出规则配置属于 高级话题 的内容,现阶段通常用于流程表单中做复杂提交,配置片段参考如下:

    {
        "io": {
            "writer": {
                "record": {
                    "inSource": "linkageAsset",
                    "inPath": "targetData",
                    "outType": "ARRAY"
                }
            }
        }
    }
    • 输入 表示表单加载过程中的数据初始化流程。

    • 输出 表示表单提交过程中的数据收集流程。

    此部分内容在后续实战中逐一讲解,一般使用场景如下:

    • 字段数据格式和提交到远程数据格式不完全对齐的情况下,依赖 io 规则补充、追加、转换部分字段数据。

    • 针对数据本身 Array / Object 两种不同数据格式之间的切换,以适配提交数据。

    • 您可以将 io 规则理解成表单生命周期中的 AOP 行为,可自定义 加载、交互、提交 等不同生命周期的表单数据行为。

字段属性

字段属性在 Zero UI 中包含两个:

  • ui:针对可见字段属性的定义。

    此属性的数据结构是一个二维数组:

    [
        [
        ],
        [
        ]
    ]

    二维数组描述的是 Grid 布局的基本骨架:

    • 一维表示的是当前表单有多少行,rowConfig / rowClass 就是作用于一维配置。

    • 二维表示的是某一行内的字段信息,由于是一个 Array 结构,所以有多少 {} 单元格配置元素当前行就包含多少字段。

  • hidden:不可见字段的属性定义,hidden 不参与布局。

安全属性

安全控制关注的属性是 op,此属性描述了当前表单中操作的 安全定义,现阶段的版本还没完全实现表单的细粒度控制,只要按钮可以呈现那么对用户而言就可以触发表单的提交,而 op 属性可直接实现和 S_ACTION 的绑定关系,这样绑定之后会造成用户在点击提交时可能会触发 403 异常,简单说就是:您可以看到这个表单,但是当你触发提交操作时,无权限访问接口

回调属性

回调属性和Ajax中的回调属性如出一辙,定义的是表单提交完成之后的回调行为:

  • 弹出成功弹窗提示操作成功。

  • 显示成功消息提示。

回调属性的配置片段如下:

{
    "modal": {
        "success": {
            "open": "您好,您的流程申请已成功提交,申请单号 :serial,请等待审批。",
            "draft": "您好,您的流程申请已成功保存为草稿!单号为 :serial。"
        }
    }
}

回调属性是新版提交按钮的配置,在新版按钮配置中,您可以设置 原子属性closable=true,callback=open,设置了上述原子属性之后,表示:

  • 当前表单提交之后触发 rxClose 函数,通常是:关闭弹窗关闭页签关闭页面 等行为。

  • 弹窗内的文字显示 modal 定义中的 open 对应文字,即上边示例中的:您好,您的流程申请已成功提交,申请单号 :serial,请等待审批。,其中消息中的参数会被 response 中对应的数据替换。

骨架代码

若您配置了上述的表单之后,可直接书写骨架代码来完成表单的整体组件开发。

源代码 UI.js
import React from 'react';
import Ux from 'ux';
import Ex from 'ex';
import {ExForm} from 'ei';
import Op from './Op';

@Ux.zero(Ux.rxEtat(require('../Cab'))
    .cab("UI.Add")
    .to()
)
class Component extends React.PureComponent {
    render() {
        const form = Ex.yoForm(this, null);
        /*
         * 如果是更新,上述代码改成
         * const {$inited = {}} = this.props;
         * const form = Ex.yoForm(this, null, $inited);
         */
        return (
            <ExForm {...form} $height={"300px"}
                    $op={Op.actions}/>
        );
    }
}

export default Component;

针对上述代码说明几点:

  1. 一般表单开发会使用 ei 库中的 ExForm 组件,此组件还会帮助您完成各种智能模式处理(已在生产环境验证很高频的用法,减少代码量)。

  2. 表单和资源文件绑定之后,会直接从 cab/cn/ 目录下抓取 名空间 对应的JSON配置文件。

  3. 此处的行为模式主要绑定 Op.js 中的 actions 变量,此变量定义在下边有说明。

Zero UI中不推荐将文本呈现放到 JS 文件中,通常一个组件会包含两部分:

  • 资源文件绑定部分:cab/cn/ 下的资源文件定义,此处的资源文件定义实际在处理组件配置时会实现组件配置分流

    • 前端资源文件绑定,根据 Z_LANGUAGE 抓取资源文件目录实现 多语言 架构,通常是 OOB 专用的方式。

    • 后端静态资源绑定,若是 项目 静态开发模式,直接从后端 src/main/resources/cab/cn/ 中读取资源文件。

    • 后端动态资源,直接启用后端的 zero-ui 模块从 UI_X 系列表中读取动态配置。

不论是前端还是后端都可以直接在配置提取过程中读取不同的 语言资源文件,这样的模式下会保证您的界面实现最大限度的灵活定制,若想要从 中文 切换到 英文,只需要更改资源文件信息即可,不用对源代码进行任何调整。

操作代码 Op.js
import Ex from 'ex';

const $opAdd = (reference) => params => Ex.form(reference).add(params, {
    uri: "/api/role",
    dialog: "added",
});
const $opSave = (reference) =>
    params => Ex.form(reference).save(params, {
        uri: "/api/role/:key",
        dialog: "saved"
    });
const $opDelete = (reference) =>
    params => Ex.form(reference).remove(params, {
        uri: "/api/role/:key",
        dialog: "removed"
    });
const $opFilter = (reference) =>
    params => Ex.form(reference).filter(params);

export default {
    actions: {
        $opAdd,
        $opSave,
        $opDelete,
        $opFilter
    }
}

上述代码是最简单的角色管理,由于没有任何自定义逻辑,所以此处可直接使用 Ex.form(reference) 的方式实现相关提交,而配置使用了标准化按钮配置:

按钮ID 含义

$opAdd

添加按钮专用表单函数,mode=ADD

$opSave

更新按钮专用表单函数,mode=EDIT

$opDelete

删除按钮专用表单函数。

$opFilter

查询表单专用函数,右侧抽屉搜索框专用。

最后需要注意的一点是此处的函数本身具有一定的基本规范

  1. 此处的函数定义的是一个 二阶函数,函数签名如下

    const $opXxx = (reference) => (params) => {
    
    }
    • reference 是和操作绑定的外层组件的引用,需要开发人员注意引入外层组件之后的层级关系,可通过 Ux.onReference 的方式精确捕捉所需组件。

    • params 是 Ant Design 中的表单提交之后的数据。

  2. 此处的操作函数返回一个 Promise,返回 Proimse 的原因很简单:同步/异步 统一,而且往往这种类型的提交都会和后端互动,所以使用 异步 比同步的实用性更强。

  3. Ex.form 的表单对象引入了默认的 防重复提交、默认回调、异常验证、异常回调、规则转换 等综合性功能,除非您十分了解,否则只能自己老老实实书写自己的函数,如(自定义流程):

    const $opCreate = (reference) => (params) => {
        const request = Fn.ioRequest(params);
        let category = {};
        if (request.grouped) {
            category = Ux.elementUniqueDatum(reference, "preorder.category", "code", "Company");
        } else {
            category = Ux.elementUniqueDatum(reference, "preorder.category", "code", "Personal");
        }
        if (category) {
            request.category = category.key;
        }
        return Ux.ajaxPost("/api/order/standard/submit", request)
            .then(data => Ux.ajaxDialog(reference, {
                data, key: "submitted", redux: true,
            }))
            .then(response => Ux.formReset(reference, [], response));
    };

    由于此处没有使用 Ex.form 的表单对象,所以回调中的 Ux.ajaxDialog 以及执行完成之后的表单重置 Ux.formReset 都是必须的。

按照本章的教程,基本开发人员只需要 拷贝 骨架代码,然后书写对应的行为函数( Function )就可以完成表单部分的开发,且不需要书写太多的表单规则或逻辑规则,接下来看看表单中常用的 字段配置。 :data-uri: :table-caption!:

高级话题

本章节难度比较大,后续在开放 开发中心 时再一一补充,完成教程书写,现阶段开发人员可暂时略过。

数据转换

此部分内容为实验性版本,现阶段除了流程表单中存在 io 配置,其他位置的配置并没有出现,还存在待改进以及不稳定的情况存在。但 io 这个设计会延续,此设计方便开发人员直接配置 页面流,在读写( input / output )两个生命周期内注入 规则、插件、函数 来完成数据转换相关的定制。

Zero 表单配置新版本中中加了特殊配置来执行数据的进出控制,主要处理表单的 加载和提交 的生命周期,让您可以针对表单的数据流程执行细粒度控制,先参考下边两段配置:

io

{
    "io": {
        "writer": {
            "record": {
                "inSource": "linkageAsset",
                "inPath": "targetData",
                "outType": "ARRAY"
            }
        }
    }
}

transform(将被 reader 代替)

此部分位于代码:src/utter/channel.__.v.yo.transformer.js 源码。

{
    "transform": {
        "record@file": {
            "type": "FILE",
            "field": "record@fileKey",
            "fieldName": "record@name"
        },
        "record@sizeUi": {
            "type": "SIZE",
            "field": "record@size"
        }
    }
}

后期的基本设想是直接使用 io 配置完成所有的读写流程,参考 io 在表单生命周期中存在的位置:

exp form io

writer

writer 中记录了每一个字段的基础配置,此处配置的数据结构如下:

配置项 含义

inField

固定值和别名:

  • inField 未配置,那么此处就是 writer 配置对象的 key

  • inField 已配置,此处配置的值会成为 key 的别名来执行处理。

inSource

当前配置属性采集的数据源属性,此处由于是提交内操作,所以源头默认为当前表单已提交的数据。

inPre

条件检查配置项,它所定义的条件满足的情况下才能触发当前属性的 io 操作。

inPath

从当前采集数据的数据源中提取路径下的内容,此属性支持 表达式,前端会根据 Ux.formatExpr 对路径执行格式化处理。

outType

转换之后的数据类型

  • ARRAY:输出类型为 []

  • OBJECT:输出类型为 {}

  • (未设置):输出原始类型或原始数据。

此处的 writer 的执行流程如:

exp form io.writer

所以上述 writer 的执行流程如:

  1. 从表单提交数据中提取 linkageAsset 属性,此处属性数据类型是 [] 数组类型。

  2. 跳过 inPre 步骤检查。

  3. 从提取到的属性中抓取 targetData 部分的值

    • 如果提取源是 [] 类型,那么此处做的是 投影提取

    • 如果提取源是 {} 类型,那么直接提取 key = targetData 部分的值。

  4. 最终输出是 ARRAY,会构造 [] 的输出类型。

io 的全称是 input / output,意为表单生命周期中 加载提交 两个生命周期,有了此处的配置,表单本身的行为有了很大的扩展空间。

reader

(暂时还未使用,教程滞后)。 :data-uri: :table-caption!:

表单组合

前文中提到的是 _form 配置节点,除了此节点以外,表单配置还支持 按行组合 的方式对表单碎片 从上到下 的布局模式,这种模式属于 表单组合(参考 Ex.yoForm 函数):

每一个表单都遵循本章的表单结构,系统会将 ui 按顺序合并,下边表格是按默认顺序枚举的表格,从上到下的流式布局,其中 additional.formRules 是一个 [],可以按照 编码 重新定义表单片段的顺序。

additional 在很多前端API都出现过,一般前端的API的默认常用术语如下:

  • data:数据变量,存储了当前API处理的数据信息,dataJ - Object、dataA = Array

  • config / options:配置变量,存储了当前API处理数据依赖的配置数据信息,一般用 config,部分场景中使用 options

  • additional:附加配置,通常是 编程模式 专用配置变量,当某些API依赖外层调用编程方式传入配置时,用此变量。

表单配置中就是使用了 additional 作编程兼容,而 data / config 都来自 React 组件的 props 中。

表单片段完整如:

配置项 编码 含义

additional.form

FP

(编程模式)Form Program:表单配置中第一个片段,依赖 yoFormadditional 参数。

_formUp

FU

(上半部分)Form Up:前端 Cab 对应的资源文件中的定义,配置模式表单。

props.$options.formUp

PFU

(上半部分)Prop Form Up:从组件属性传入的表单配置。

state.$formS.formUp

SFU

(上半部分)State Form Up:一般是服务端配置专用,访问远程Ajax拉取表单片段。

_form

FF

(主表单)Form Focus标准配置,前端 Cab 对应资源文件中定义的标准表单配置。

props.$options.form

PFF

(主表单)Prop Form Focus:从组件属性传入的表单配置。

state.$formS.form

SFF

(主表单)State Form Focus:一般是服务端配置专用,访问远程Ajax拉取表单片段。

state.$formW

WFF

(工作流)Workflow Form Focus扩展,工作流专用表单配置。

_formDown

FD

(下半部分)Form Down:前端 Cab 对应的资源文件中定义,配置模式表单。

props.$options.formDown

PFD

(下半部分)Prop Form Down:从组件属性传入的表单配置。

state.$formS.formDown

SFD

(下半部分)State Form Down:一般是服务端配置专用,访问远程Ajax拉取表单片段。

参考下边的表单组合原理图理解此处的表单合并过程:

exp form segment

从上述原理图解析:

  1. 编程模式直接扩展通常使用 additional 中的配置,优先级比较高,除此之外,若是自定义组件,编程模式还可以:

    • 父组件更改配置之后传入子组件中填充 $options 变量,上层编程配置。

    • 当前组件更改状态之后更新 $formS 变量( Server Form ),本层编程配置。

  2. 标准处理中 $formS 的含义为 Server Form,即通常用于从远程 Ajax 接口拉取表单配置,服务端三种配置一般如:

    • 动态配置:这种模式不启用 表单组合 功能,纯动态配置,上边原理图会失效。

    • 组合配置:这种模式中通常会拉取远程 X_MODULE 中的配置并执行解析,最终会将服务端的表单配置存储到 $formS 变量中。

    • 工作流:这种配置只有一个 扩展,目前是工作流专用,和工作流中的节点绑定的专用配置。

  3. 顺序调整:上边提供的编排是 Zero Ui 提供的默认表单组合配置,您也可以在编程过程中更改 formRules 根据 编码 更改您想要的表单片段。

  4. 工作流 之外,所有的表单源都提供了:上、中、下 三种模式,再结合:编程、前端配置、后端配置 三种情况,实际形成了一个表单组合的九宫格,这种模式下已经可以满足大部分表单的组合流程了。

表单组合 这种模式只适用于行交换,简单说不可以做单行中的列切换,如某一行有:姓名、电话、邮箱,那么当它在 表单组合 中切换时,这三个属性的行顺序不可以变更。

若对表单执行切分,原子级 的单元是属性(字段),而在 Zero Ui 中由于有 Grid 布局,又多了一级:行级表单组合 实际在 行级 之上为表单又创建了一级:区域级,这样设计的目的实际是简化开发和配置工作量,下边两种场景不适合 表单组合

  • 动态配置:若您的表单以来后端 zero-ui 中的 UI_FORM 来执行配置,那么此时不可以使用 表单组合,这种场景下由于定制的粒度已经可以到 原子级,所以不再依赖 表单组合 来实现复杂的表单布局,这种模式是最好的,但依赖 开发中心 和配置结果。

  • 具象编程:这种模式就是表格中的 标准模式,一般模型是固定的,不依赖任何扩展、继承、多态的概念,表单后台绑定的模型本身已经属于 不用拆分 的场景,所以这种情况也不依赖 表单组合,目前大量的开发都是这种模式。

表单组合 是 Zero Ui 提供的兼容了纯动态和纯编程两种模式的一种折中形态,折中形态一方面可以减少编程量和配置量,另外一方面表单本身可以使用“搭积木”的方式来完成,表单内置实现了模块化布局。

表单碎片

表单碎片 和表单组合一样,同样是开发表单布局使用的内容,现阶段只用于 工作流管理,教程暂时不梳理,后续版本落地成型后再追加到白皮书中(目前版本比较脆)。

字段配置

字段配置是表单配置中的重头戏,参考前文提到的字段解析器,实际字段配置中会包含如下特殊的属性:

配置属性 含义

metadata / field

当前字段的字段名,有两种配置模式

  • metadata:启用属性解析器的模式,这种模式下可以参考前文提到的解析信息。

  • field:全格式,这种模式不启用 属性解析器,需将 JSON 部分内容全部配置完整。

optionConfig

此属性服务于早期 AntD 中的双向绑定相关配置,目前虽然升级但也沿用下来了此配置,方便开发人员理解。

optionItem

此属性主要对应到 <Form.Item/> 中的设置,文字、宽度、布局、风格等相关信息。

optionJsx

此属性为输入控件专用属性,对应到真正的 AntD 中的表单组件中(原生映射)。

moment

当前字段是否时间格式,理论上此处不应该如此设置,但由于 optionXxx 都是直接和 AntD 绑定,所以此处不能再将 moment 属性转移到内置属性中,就只能沿用此处属性了。

span

当前字段在 Grid 布局中的宽度。

表单的字段配置主要位于:

{
    "ui": [
        [
            "字段定义",
            {
                "comment": "字段定义"
            }
        ]
    ]
}

optionConfig.rules 会在单独的 验证规则 章节说明,此处就不累赘。

  • 本章只讲解各种字段基础配置,若找不到示例代码可能由于字段本身不常用,这种场景教程不会完全覆盖。

  • 标题中带 (A) 标记的是 AntD 的原生组件。

  • 每个章节中的 特殊配置 是根据 AntD 和 Zero UI 结合的实用性配置,和 AntD 不同的点在于 Zero UI 几乎全程是配置驱动。

  • 部分复杂的 高频 使用组件我会在教程中附上对应截图,让开发人员对 Zero UI 中的表单类 交互式组件 有个感性的认识,截图由零点科技有限公司(使用Zero的友人)提供。

aiInput (A)

最简单的文本渲染,对应 AntD 中的 <Input/> 组件,也是默认值(即不配置也可),配置如下:

{
    "metadata":"name,名称,14,,,placeholder=20个中文字符,normalize=text:40",
    "optionConfig.rules":[
        "required,请输入名称,名称不可为空!",
        {
            "validator": "existing",
            "message": "对不起,名称重复!",
            "config": {
                "uri": "/api/role/existing",
                "method": "POST",
                "params": {
                    "sigma":"PROP:app.sigma"
                }
            }
        }
    ]
}

上述代码取自 角色管理,先忽略 optionConfig.rules 部分的验证规则配置,若去掉验证规则此处的配置有如下写法(注意此处的 [] 是第二个维度,并非最上层维度):

写法一

[
    "name,名称,14,,,placeholder=20个中文字符,normalize=text:40"
]

写法二

[
    {
        "metadata": "name,名称,14,,,placeholder=20个中文字符,normalize=text:40"
    }
]

一般情况若不开启验证规则而 原子解析器 又可直接生效的场景中不采用第二种 metadata 的写法,第一种写法更省事,且注意所有 $KV$ 部分必须在定义的表单字段解析器格式之后,从索引 5 开始书写 key=value原子解析 模式。上述配置中若没有后续的 原子解析 信息可直接书写成:"name,名称,14"。由于 aiInput 是默认值,所以此处带 render 的写法应该是:"name,名称,14,,aiInput"。上述配置最终解析的完整配置如( 后续配置 不再开放完整格式,大家自行阅读。):

{
    "field": "name",
    "optionItem": {
        "label": "名称"
    },
    "optionConfig": {
        "normalize": "text:40"
    },
    "optionJsx": {
        "placeholder": "20个中文字符"
    },
    "span": 14
}

输入框如下:

0

aiHidden

aiHidden 直接作用于表单配置中的 hidden 节点,通常在字段配置中是不需单独配置的,属于最特殊的渲染器。

aiInputArray

  • 此渲染器有别名 aiInputMulti,旧版使用 aiInputMulti,以后所有开发中新版推荐使用 aiInputArray(原组件名)。

多选项输入模式,此字段渲染使用了 web 库中的 <InputArray/> 组件,此组件可以帮助开发人员实现多行文本输入,且产生 [] 的表单值。配置如下:

{
    "metadata": "dependValue,条件字段值,24,,aiInputMulti",
    "optionJsx.styleInput": {
        "width": "30%"
    },
    "optionJsx.depend.enabled": {
        "dependType": [
            "DATUM"
        ]
    }
}

此处不深究 optionJsx.depend依赖和影响 配置,这个组件中会包含一个操作按钮,您可以点击这个按钮动态追加输入文本行,每行文本会生成一个 Array 的项,最终生成形如:

[
    "文本行一的文字",
    "文本行二的文字"
]

aiInputNumber (A)

对应 AntD 中的 <InputNumber/> 组件,用于 数值 录入。配置如下:

[
    "layoutLeft,输入框宽度,,100%,aiInputNumber,min=1,max=24",
    "layoutRight,选择框宽度,,100%,aiInputNumber,min=1,max=24"
]
特殊配置

此组件会多一个配置 optionJsx.numeric,其数据结构如下:

{
    "percent": "是否执行百分比计算",
    "unit": "单位信息",
    "unitPosition": "单位位置"
}

配置属性表:

属性路径 含义

optionJsx.numeric.percent

布尔值,若此值为 true 则数值会转换成带有 % 符号的数据。

optionJsx.numeric.unit

数值单位,用于表示当前数值携带的计量单位相关信息。

optionJsx.numeric.unitPosition

布尔值,若为 true,格式为 {value} {unit},若为 false,格式为 {unit} {value}

aiTextArea (A)

对应 AntD 中的 <Input.TextArea/> 组件,用于多行文本录入,默认场景下多行文本录入由于追加了 textarea 的 CSS 是不允许做尺寸更改的,这也是 Zero UI 中的限定,尺寸的更改有可能会更改布局导致问题。配置如下:

[
    {
        "metadata": "comment,备注,24,,aiTextArea,maxLength=1024",
        "optionJsx.rows": 3
    }
]

多行文本常用的两个属性如 rows, maxLength,不过这两个属性都不属于 特殊属性,实际是 AntD 原生属性。 :data-uri: :table-caption!:

aiPassword (A)

对应 AntD 中的 <Input.Password/> 组件,主要用于密码录入,密码输入框有一个默认属性 visibilityToggle,此属性可用于切换密码的 显示和隐藏,若是只读模式自动隐藏。配置如下:

[
    {
        "metadata": "opassword,旧密码,14,,aiPassword",
        "optionConfig.rules": [
            "required,请输入旧密码",
            {
                "min": 8,
                "message": "密码长度必须大于8个字符!"
            }
        ]
    }
]

密码框如下:

0

aiProtocol (A)

对应 AntD 中的 <Input/> 组件的一个变种,您可以直接录入带协议的URL地址,然后点击 解析 按钮,系统会根据 linker 的配置将协议拆分之后的数据填充到现有的表单中。

[
    {
        "metadata": "endpoint,FTP地址,16,99%,aiProtocol,placeholder=上传文件的完整路径,可直接使用ftp命令的路径地址。",
        "optionJsx.config.linker": {
            "hostname": "hostname",
            "port": "port",
            "username": "username",
            "password": "password",
            "path": "path"
        }
    }
]

上述配置存在于集成管理处理 RESTful、FTP 协议部分,您可以填写如下:

http://ox.engine.cn:8081/api/app

上述内容会被直接解析成:

{
    "hostname": "ox.engine.cn",
    "port": 8081,
    "username": null,
    "password": null,
    "path": "/api/app"
}

由于此处处理的是 RESTful,所以 usernamepassword 在此处无法解析,为空,若是 FTP 协议或其他协议,此处会解析得到对应的账号以及密码。

aiRadio (A)

对应 AntD 中的 <Radio.Group/> 组件,您可直接设置单选框信息,其配置如下:

[
    {
        "metadata": "method,HTTP方法,12,,aiRadio",
        "optionJsx.config.items": [
            "GET,GET方法",
            "POST,POST查询"
        ]
    }
]
选项说明

选项在 Zero UI 中主要使用在 aiRadio, aiSelect, aiCheckbox 三种最常见的 选择组件 中,这三种组件在 配置选项 时其用法一样:

  1. 静态配置,静态配置如示例中设置的,直接配置 optionJsx.config.items 属性,此属性是一个 [] 结构,数字的每一个元素可直接解析成一个选项,如上述示例中会直接解析成:

    [
        {
            "key": "GET",
            "value": "GET",
            "label": "GET方法"
        },
        {
            "key": "POST",
            "value": "POST",
            "label": "POST查询"
        }
    ]
  2. 动态配置,动态配置一般会使用字典配置 optionJsx.config.datum 属性,此属性通常会书写成 String 的字符串格式:

    {
        "metadata": "category,组类型,14,,aiRadio",
        "optionJsx.config.datum": "source=ajax.groups.type,value=code,label=name",
        "optionJsx.config.datumSort": {
            "field": "sort",
            "asc": true
        }
    }

    上述配置中,datum 配置了字典作选项的基本信息,而且还配置了额外的 datumSort 来处理选项的排序问题。一般情况选项会按照字典默认读取的顺序排列,若读取远程接口没有在服务端执行排序,那么就可以启用此处的 datumSort 属性按某个属性进行选项的排序。

单选框如下:

0

aiSelect (A)

对应 AntD 中的 <Select/> 组件,您可以直接设置下拉信息,配置如( 此处使用静态完整配置 ):

[
    {
        "metadata": "type,数据类型,19,,aiSelect,placeholder=必选",
        "optionJsx.config.items": [
            {
                "key": "java.lang.String",
                "label": "字符串"
            },
            {
                "key": "java.lang.Integer",
                "label": "整数"
            },
            {
                "key": "java.lang.Long",
                "label": "长整型"
            },
            {
                "key": "java.lang.Boolean",
                "label": "布尔值"
            },
            {
                "key": "java.math.BigDecimal",
                "label": "浮点数"
            },
            {
                "key": "java.time.LocalTime",
                "label": "时间格式"
            },
            {
                "key": "java.time.LocalDate",
                "label": "日期格式"
            },
            {
                "key": "java.time.LocalDateTime",
                "label": "日期/时间格式"
            }
        ],
        "optionConfig.rules": [
            "required,请选择数据类型!"
        ]
    }
]

aiCheckbox (A)

对应 AntD 中的 <CheckBox.Group/> 组件 或 <Switch/> 开关组件,一般情况下二选一的模式是最常用的。

两种模式之间的切换依赖 optionJsx.mode 属性,若此属性设置为 SWITCH 的值切换到 开关 模式,反之若使用 CHECKBOX(默认值不用填写)则切换到 勾选 模式。

启用/禁用

此组件最常用的一种模式是单个 CheckBox 的勾选:

[
    {
        "metadata": "active,启用,14,,aiCheckbox",
        "optionConfig": {
            "valuePropName": "checked"
        }
    }
]

上述模式几乎在每个表单中都存在,用于设置 启用/禁用 相关状态。

开关配置

除开上述的 勾选 之外,此组件还支持 开关 模式,配置如下:

[
    {
        "metadata": "isHidden,是否隐藏,8,,aiCheckbox",
        "optionJsx": {
            "mode": "SWITCH",
            "checkedChildren": "隐藏按钮",
            "unCheckedChildren": "显示按钮"
        },
        "optionConfig": {
            "valuePropName": "checked"
        }
    }
]
多选配置

多选配置和 aiSelect / aiRadio 设置选项一样,此处就不重复讲解了,设置下边属性可开启多选:

  • optionJsx.config.items

  • optionJsx.config.datum

aiDatePicker (A)

对应到 AntD 中的 <DatePicker/> 组件,可直接进行日期选择,配置如下:

[
    {
        "metadata": "signedAt,签订时间,,,aiDatePicker,placeholder=选择签订时间",
        "optionJsx.format": "YYYY-MM-DD HH:mm",
        "optionJsx.showTime": true,
        "moment": true
    }
]

日期选择器如下:

0

特殊配置

Zero UI中的日期选择器唯一的特殊配置是指定属性 moment=true,早期的 AntD 中的时间格式使用的是 moment.js 的库,这个库和新版的 Dayjs 库一样,对 undefined 是会报错的,而单纯从数据层面无法根据格式或其他值来判断当前属性是否 日期/时间,所以只能在表单这一级直接定义当前属性是一个 moment=true 的日期格式数据。再者日期格式除了此处的 <DatePicker/> 以外,还会有独立时间 <TimePicker/> 选择器,所以系统无法根据 moment.js 执行反算;其实真计算是可以的,但由于组件本身是逐渐开发出来的,<DatePicker/><TimePicker/> 之前,所以就懒得在原始组件上修改了,最终在组件上保留了 moment 这个属性来鉴别 时间/日期 类型的控件。

aiTimePicker (A)

对应到 AntD 中的 <TimePicker/> 组件,可以直接进行时间选择,配置如下:

[
    "pTimeArrive,默认到店时间,,,aiTimePicker,format=HH:mm",
    "pTimeLeave,默认离店时间,,,aiTimePicker,format=HH:mm",
    "pOrderKeep,无效订单保留,,,,addonAfter=天"
]

时间选择器默认会被 Zero UI 直接追加 moment / true 的属性,所以后期升级之后此属性也可以逐渐被拿掉。时间选择器如下:

0

上传图标专用组件,此组件执行过封装,底层使用了 AntD 中的 <Upload/> 组件,但通过限定属性让此组件只拥有 图片上传/预览 的功能。配置如下:

[
    {
        "metadata": "logo,应用Logo,16,,aiFileLogo,listType=picture-card,text=上传",
        "optionJsx.config.filekey": "key",
        "optionJsx.config.limit": 10240,
        "optionJsx.ajax.uri": "/api/file/upload/:identifier?category=:category",
        "optionJsx.ajax.download": "/api/file/download/:key",
        "optionJsx.ajax.params": {
            "identifier": "FIX:x.application",
            "category": "FIX:logo"
        }
    }
]
特殊属性

一般自定义组件都会包含部分定制过的 特殊属性,毕竟 Zero UI 中的所有自定义组件都需要支持纯描述的 配置模式

配置属性表:

属性路径 含义

optionJsx.config.fileKey

此处的 fileKey 会将后台生成的唯一的标识写入到 key 参数中,这个参数和 optionJsx.ajax.download 中配置下载参数一致( :key 参数);fileKey 在后端是唯一文件标识,您可以使用此属性从后台下载到上传过后的文件。

optionJsx.config.limit

上传文件的限制,单位为 KB,如上边示例中上传文件限制为 10M

optionJsx.ajax.uri

上传专用路径信息,可包含类似 :identifier:category 参数,这些参数会从参数节点提取。

optionJsx.ajax.download

可根据上传文件生成对应文件的下载链接,下载链接是一个唯一的 URI 地址。

optionJsx.ajax.params

参数配置,此参数主要针对 上传 行为执行定制,而且会影响上传的最终存储 集成目录

图片上传组件如下:

0

aiFileBatch

批量上传组件,底层使用了 AntD 中的 <Upload/> 组件,配置如下:

[
    {
        "metadata": "files,文件上传,18,,aiFileBatch,text=上传",
        "optionJsx.accept": "*/*",
        "optionJsx.config.filekey": "key",
        "optionJsx.config.limit": 10240,
        "optionJsx.config.reduce": true,
        "optionJsx.ajax.uri": "/api/file/upload/:identifier?directory=:directory",
        "optionJsx.ajax.download": "/api/file/download/:key",
        "optionJsx.ajax.params": {
            "identifier": "FIX:x.notice",
            "directory": "/系统文档/公告文档",
            "formula": "/${name}"
        }
    }
]
特殊属性

(此组件特殊属性和前文 aiFileLogo 保持一致)。

此处要特别说明的是 directory 参数,此参数会开启上传文件过程中的 集成存储 功能,它配置了文件最终存储地址,若一个文件挂载了某个目录(带有 directory 参数),那么这个目录将会映射到后台作为 I_DIRECTORY 中的配置指向存储,若此目录开启了集成功能,那么后台存储此文件的有可能是 FTP地址、SSH文件系统、NFS地址 等,且示例中还包含了 formula 参数,该参数可支持 动态目录 功能,在某个 directory 之下您可以根据输入的数据创建内部嵌套目录(流程附件依靠的就是此参数的动态设置)。

批量上传组件如下:

0

aiFileUpload

批量上传组件,底层使用了 AntD 中的 <Upload/> 组件,配置如下:

[
    {
        "metadata": "record@file,文件下载,16,,aiFileUpload,text=上传",
        "optionJsx.accept": "*/*",
        "optionJsx.config.filekey": "key",
        "optionJsx.config.limit": 10240,
        "optionJsx.ajax.uri": "/api/file/upload/:category",
        "optionJsx.ajax.download": "/api/file/download/:key",
        "optionJsx.ajax.params": {
            "category": "FIX:FILE.REQUEST"
        },
        "optionJsx.config": {
            "linker": {
                "key": "record@key",
                "fileKey": "record@fileKey",
                "name": "record@name",
                "type": "record@type",
                "size": "record@size",
                "sizeUi": "record@sizeUi",
                "extension": "record@extension"
            }
        },
        "optionConfig.rules": [
            "required,上传文件不可为空,请上传您要审批的文件!"
        ]
    }
]

上述配置是 文件流程 中的特殊配置,前文中已经针对 特殊配置 有所说明了,此处先不讲 linker(放到后续专用组件中讲解)。

子字段 @

此处需要讲解一个特殊的 Zero UI 中的表单语法,通常模式下一个表单字段是类似 name 这种格式,但随着集成服务 zero-is 开启之后,Zero UI做了一次升级,本次升级中,让表单 <Form/> 的属性支持嵌套型,但由于 . 已经被 属性解析器 使用了,所以这种模式下使用 @ 做分隔符来区分主记录和子记录,如本示例中的 record@key,实际这些属性最终会执行转换。

{
    "record@key": "key1",
    "record@name": "name1",
    "record@size": 96
}

上述格式最终会转换成:

{
    "record": {
        "key": "key1",
        "name": "name1",
        "size": 96
    }
}

上述转换流程会在表单提交过程中自动转换,且和表单绑定时也会自动执行,您无需做任何操作就可以实现这种级别的数据转换行为。 :data-uri: :table-caption!:

aiGroupSwitcher

此组件为 双态组件,Zero UI中的自定义组件,配置如下:

[
    {
        "metadata": "openGroup,工作组,8,,aiGroupSwitcher,readOnly=true",
        "optionJsx.config.datum": "source=ajax.groups,value=key,label=name",
        "optionJsx.config.bind": "groupOpen"
    }
]

此组件的配置中直接和字典绑定了,配置了 optionJsx.config.datum 的值,这个组件最终会有两个状态:

  • 若数据只有一条:只读状态和唯一的数据产生绑定,呈现相关信息,此时是只读状态。

  • 若数据有多条:直接使用下拉可选择合法的值,此时是可编辑状态。

特殊属性

配置属性表:

属性路径 含义

optionJsx.config.bind

值捆绑配置,此处设置为 groupOpen,即设置了值之后,openGroup 属性的值话 groupOpen 的值维持一致。

aiCaptcha

验证码专用自定义组件,此组件用于呈现验证码并生成验证码。配置如下(登录页专用,全配置格式):

[
    {
        "field": "captcha",
        "$render": "aiCaptcha",
        "optionConfig": {
            "rules": [
                {
                    "required": true,
                    "message": "请输入验证码!"
                }
            ]
        },
        "optionJsx": {
            "placeholder": "验证码",
            "prefix": {
                "type": "code"
            },
            "config": {
                "type": "image",
                "ajax": {
                    "uri": "/captcha/image",
                    "method": "POST"
                },
                "error": {
                    "501": "对不起,您的服务端不支持验证码功能!"
                }
            }
        },
        "optionItem": {
            "hasFeedback": true
        }
    }
]
特殊属性

配置属性表:

属性路径 含义

optionJsx.config.type

当前验证码的基本类型,默认使用图 image 的方式。

optionJsx.config.ajax

远程接口专用配置,若要支持验证码功能那么验证码本身必须从服务端生成并呈现在客户端,验证码在点击时会重新发送请求自动刷新,Zero Extension中默认路径使用 /captcha/image 接口。

optionJsx.config.error

如果出现了错误则直接以异常的方式呈现在界面上,如:服务端不支持等。

验证码组件如下:

0

aiJsonEditor

标准的Json编辑器,内置使用可格式化高亮的Json,配置如下:

[
    {
        "metadata": "options,额外配置,24,,aiJsonEditor",
        "optionJsx.height": 160
    }
]

此组件除了高度以外其他内容不用设置,最终提交出来的数据为 {}[] 的 JSON 格式,一般此编辑器是用来编辑配置文件专用的交互式控件。

aiBraftEditor

富文本编辑器,提供了可支持 HTML 格式的编辑器功能,配置如:

[
    "content,公告内容,18,,aiBraftEditor"
]

编辑器如:

0

aiAddressSelector

专用地址选择器,用来选择:国家/省会/城市/二级市 四个层级专用的选择器,由于此选择器目前在项目中没有整体使用,所以现阶段没有直接可参考的配置代码,暂不做说明,后续来补充。

aiCheckJson

JSON格式的多选组件,和 aiCheckbox 的不同点在于输出的数据格式有所区别:

  • aiCheckbox 中的选中输出值为:[v1, v2] 的格式。

  • aiCheckJson 中的选中输出值格式为 Object 的格式。

使用配置如下:

[
    {
        "metadata": "metadata,基本配置,24,,aiCheckJson",
        "optionJsx.config.items": [
            "design,可设计",
            "deletion,可删除"
        ]
    }
]

上述选择结果中,最终会生成如下数据格式:

{
    "design": true,
    "deletion": false
}

aiListSelector

(高频组件)常用的列表选择器,选择关联数据专用。配置如下:

[
    {
        "metadata": "username,关联账号,8,,aiListSelector,placeholder=(请选择账号)",
        "optionJsx.config": {
            "ajax": {
                "metadata": "POST,/api/user/search,1,10,sorter=updatedAt`DESC",
                "params.criteria": {
                    "sigma": "PROP:app.sigma",
                    "": "OPERATOR:AND"
                }
            },
            "linker": {
                "key": "userId",
                "username": "username",
                "realname": "viceName",
                "email": "viceEmail",
                "mobile": "viceMobile"
            },
            "table": {
                "columns": [
                    "username,用户账号",
                    "realname,用户名称",
                    "mobile,手机号",
                    "email,邮箱"
                ]
            },
            "validation": "请选择关联账号!",
            "window": "关联账号,选择,关闭,false,800,false",
            "search": {
                "username,c": "账号",
                "realname,c": "姓名"
            }
        },
        "optionJsx.allowClear": true
    }
]

这个组件用于从另外一张表筛选数据关联到当前属性中,属于相对比较复杂的 组件

列表选择器如下:

0

特殊属性

配置属性表:

属性路径 含义

optionJsx.config.ajax

Ajax远程通信专用配置,此处配置支持 属性解析器,且使用了查询引擎的基本语法,参考 params.criteria 部分。

optionJsx.config.linker

Zero UI中专用的 linker 配置。

optionJsx.config.table

弹出的对话框中的表格专用配置,此表格配置遵循列表配置规范,可以像 主列表 一样配置。

optionJsx.config.validation

选择验证的提示文字,启用了此组件之后,在弹出的对话框中您需要选择某一行数据做选项,若不选择则无法点击。

optionJsx.config.window

弹出框的基本配置,此配置支持 属性解析器 中的弹出窗口解析。

optionJsx.config.search

搜索框配置,您可以在弹出框中设置搜索功能,具体搜索参考查询引擎专用语法,此处生成:username like ? AND realname like ?

linker配置

Zero UI中的 linker 配置也属于高频配置,通常位于:

{
    "optionJsx.config.linker": {
        "from1": "to1",
        "from2": "to2",
        "...": "...",
        "fromN": "toN"
    }
}

上述数据结构中,fromX 表示选中记录中的属性,而 toX 则表示当前表单中的属性,当您触发了选择操作之后,就可以将对应属性拷贝到表单上(右值绑定表单),上述结构参考下图:

0

从图上可以看出 linker 的核心值拷贝原理。

列表选择器基本属于 Zero UI 开发过程中的高频组件,基本可以让你完成 双表连接 的前端交互操作,只要后端的API是准备好的,那么这种双表连接不论是否支持外键都是一种前端交互的最优选择,而且弹出的对话框本身是支持:分页、查询、跳页 等列表功能的,可以当做是一个列表的简易版本。

前端所有的 Selector 级别的配置都是双字段配置模式,简单说表单中必须包含两个字段与之对应,假设目前有一个表单属性:category,那么这个属性通常配置 Selector 时会使用:

  • categoryName:虚拟属性,此属性在后端可以没有任何模型与之对应,只是单纯用于 组件呈现

  • category:实际属性,此属性才是最终存储在模型中的值,此属性一般配置到 hidden 中。

这种做法的核心原因是早期的表单开发不成熟导致,由于本身使用过程没有出现过问题,所以就没有再重新定义组件来更改,其中常用的 Selector 包括:

  • ListSelector

  • UserSelector

  • TreeSelector

  • AddressSelector(地址选择器在某些场景中直接使用级联下来完成)

aiTreeSelector

专用树型选择器,和列表选择器的配置比较类似,只是弹出框是 的数据结构。配置如下:

[
    {
        "metadata": "bankName,上级银行(父行),8,,aiTreeSelector,placeholder=(请选择)",
        "optionJsx.config": {
            "ajax": {
                "uri": "/api/bank/by/sigma",
                "magic": {
                }
            },
            "linker": {
                "name": "bankName",
                "key": "bankId"
            },
            "selection": {
                "multiple": false,
                "checkStrictly": true
            },
            "tree": {
                "title": "name",
                "parent": "bankId"
            },
            "validation": "请选择父行!",
            "window": "选择父银行,选择,关闭,false,400,false"
        },
        "optionJsx.allowClear": true,
        "optionJsx.depend.enabled": {
            "branch": true
        }
    }
]

树型选择器如下:

0

特殊属性

配置属性表:

属性路径 含义

optionJsx.config.ajax

Ajax远程通信专用配置,此处配置支持 属性解析器,且使用了 magic 模式的 输入解析器(只有查询分析器才会使用 params.criteria 参数)。

optionJsx.config.linker

Zero UI中专用的 linker 配置。

optionJsx.config.selection

属性选择器的专用配置,您可以设置属性选择器的选择模式,包括 多选、单选

optionJsx.config.tree

针对查询的数据源的树相关配置(如何构造一颗树提供选择)。

optionJsx.config.validation

选择验证的提示文字,启用了此组件之后,在弹出的对话框中您需要选择某一行数据做选项,若不选择则无法点击。

optionJsx.config.window

弹出框的基本配置,此配置支持 属性解析器 中的弹出窗口解析。

aiMatrixSelector

矩阵选择器,更为复杂的选择器功能,参考配置如:

[
    {
        "metadata": "actions,关联操作,24,,aiMatrixSelector",
        "optionConfig.rules": [
            {
                "validator": "required",
                "message": "对不起,关联操作不可为空,请添加关联操作!"
            }
        ],
        "optionJsx.config": {
            "ajax": {
                "metadata": "POST,/api/action/search,1,10,sorter=updatedAt`DESC",
                "params.criteria": {
                    "permissionId,n": true
                }
            },
            "table": {
                "columns": [
                    "name,操作名称",
                    "code,操作编码",
                    {
                        "metadata": "method,HTTP方法,MAPPING",
                        "$mapping": {
                            "GET": "GET,download,,#268941",
                            "PUT": "PUT,edit,,#0a7bed",
                            "POST": "POST,plus,,#f6af03",
                            "DELETE": "DELETE,delete,,#e22015"
                        }
                    },
                    {
                        "dataIndex": "uri",
                        "title": "请求路径"
                    }
                ]
            },
            "validation": "请选择关联操作!",
            "window": "关联操作,选择,关闭,false,980,false",
            "search": {
                "name,c": "名称",
                "code,c": "编码"
            },
            "selection": {
                "multiple": true,
                "multipleMode": {
                    "replace": false
                }
            },
            "dynamic": {
                "dataIndex": "key",
                "title": "添加",
                "config": {
                    "text": "移除",
                    "confirm": "该操作会从当前权限中移除选中操作,确认?"
                }
            }
        }
    }
]

矩阵选择器的基本配置和 ListSelector 近似,但多了部分复杂的操作,此部分选择的交互式操作在权限管理中有说明,到时候开发中心开发 权限管理 时再回头来补充本章节的详细教程。

特殊属性

配置属性表:

属性路径 含义

aiDialogEditor

子记录编辑器,此组件主要用于编辑某个记录的自己录专用,如:订单 / 订单项 的编辑模式,且这个组件应该算是 Zero UI 中最复杂的组件,很多时候要配合容器配置来进行深度定制。参考配置如下:

[
    {
        "metadata": "permissions,,24,,aiDialogEditor",
        "optionConfig.rules": [
            {
                "validator": "required",
                "message": "对不起,权限集中包含的权限不可为空!"
            }
        ],
        "optionJsx.config": {
            "form": {
                "window": 0.16,
                "columns": 2,
                "ui": [
                    [
                        {
                            "metadata": "resourceType,资源分类,14,,aiSelect,placeholder=选择资源类型",
                            "optionJsx.config.datum": "source=resource.type,value=code,label=name",
                            "optionJsx.depend.impact": {
                                "reset": [
                                    "identifier",
                                    "modelKey"
                                ]
                            },
                            "optionConfig.rules": [
                                "required,请选择资源分类!"
                            ]
                        },
                        {
                            "metadata": "modelKey,关联模型,10",
                            "optionConfig.rules": [
                                "required,请选择该分类下的关联模型!"
                            ]
                        }
                    ],
                    [
                        "identifier,模型ID,18,,,readOnly=true"
                    ],
                    [
                        {
                            "metadata": "sourcePermission,选择现有权限,24",
                            "optionJsx.config": {
                                "titles": [
                                    "可选择的权限列表",
                                    "引用权限详情"
                                ],
                                "record": {
                                    "title": "(未选择引用权限)",
                                    "selected": "已选择",
                                    "data": {
                                        "name": "权限名称",
                                        "code": "权限代码",
                                        "identifier": "所属模型"
                                    }
                                },
                                "table": {
                                    "columns": [
                                        {
                                            "dataIndex": "name",
                                            "title": "权限名称"
                                        }
                                    ]
                                }
                            }
                        }
                    ],
                    [
                        {
                            "metadata": "name,权限名称,14",
                            "optionConfig.rules": [
                                "required,请填写权限名称!"
                            ]
                        },
                        {
                            "metadata": "code,权限编码,10,,,placeholder=推荐以 perm 开头的系统助记码",
                            "optionConfig.rules": [
                                "required,请填写权限编码!"
                            ]
                        }
                    ],
                    [
                        "comment,权限备注,18,,aiTextArea,rows=5"
                    ],
                    [
                        {
                            "metadata": "$button",
                            "hidden": true,
                            "optionJsx.extension": [
                                "$opSavePerm,保存,SAVE_ROW,primary",
                                "$opReset,重置,RESET"
                            ],
                            "span": 24
                        }
                    ]
                ],
                "hidden": [
                    "key"
                ]
            },
            "dialog": "权限设置,保存,关闭,false,1150,true,$opSavePerm",
            "op": {
                "add": "$opShowPerm"
            },
            "table": {
                "limitation": 12,
                "columns": [
                    {
                        "title": "操作",
                        "dataIndex": "key",
                        "fixed": "left",
                        "$render": "EXECUTOR",
                        "$option": [
                            {
                                "text": "编辑",
                                "executor": "fnEdit"
                            },
                            "divider",
                            {
                                "text": "移除",
                                "executor": "fnDelete",
                                "confirm": "该操作会从此权限集中移除权限信息,将权限转换成`自由权限`,确认移除?"
                            }
                        ]
                    },
                    "name,权限名称",
                    "code,权限编码",
                    "identifier,模型标识"
                ]
            }
        }
    }
]

这个组件实际是一个综合组件,它的 交互式 本体就是一个列表组件,所以会有类似 table 的配置出现,而点击 添加/编辑 时它的子组件是一个表单 <Form/>,所以此处又会包含 form 节点的配置,除去这两部分的配置之后,剩余的配置部分就很容易理解了。

子记录编辑器如下:

0

特殊配置

配置属性表:

属性路径 含义

optionJsx.config.form

子组件表单配置(遵循表单配置格式)。

optionJsx.config.dialog

弹出窗口相关配置。

optionJsx.config.op

添加按钮或附加按钮专用配置(表格右上角)。

optionJsx.config.table

交互式组件表单配置(遵循列表配置格式)。

DialogEditor 主要用于子记录编辑,配合新的 zero-crud 部分,基本到最后可以 零代码 的模式实现子记录管理功能,这种场景仅在 1:N 的场景中生效(中后台管理系统中很常见),即管理的主对象和子对象是:1:N 的关联关系,在底层是两张实体表。

aiTableEditor

表格编辑器,和 DialogEditor 类似,不同点在于表格编辑器不会有弹出框,通常直接在表格中编辑相关数据。配置如下:

[
    {
        "metadata": "payment,付款方式,24,,aiTableEditor",
        "optionJsx.config": {
            "format": {
                "type": "ARRAY",
                "keyField": "name"
            },
            "table": {
                "className": "ux_table_secondary",
                "columns": [
                ·   {
                        "metadata": "quantity,数量,ROW",
                        "$config": {
                            "field": "aiInputNumber",
                            "jsx": {
                                "min": 1
                            }
                        }
                    },
                    {
                        "metadata": "price,单价,CURRENCY"
                    },
                    {
                        "metadata": "amount,金额,TOTAL",
                        "$config": {
                            "currency": "¥",
                            "op": "M",
                            "field": [
                                "quantity",
                                "price"
                            ]
                        }
                    }
                ]
            }
        }
    }
]

当前组件中没有需要特殊说明的属性,基本遵循 table 的列表配置,但是此处需要讲一下 optionJsx.config.format 配置,这个配置为表格编辑器定义了数据的基本格式,如:

{
    "type": "ARRAY",
    "keyField": "name"
}
  • type 定义了当前编辑器提交的数据格式,此处支持的数据格式如下:

    格式 含义

    OBJECT

    标准的 Json Object 格式,对应 JavaScript 中的 {}

    ARRAY

    标准的 Json Array 格式,对应 JavaScript 中的 [{},{}]

    ARRAY_MAP

    直接将一个 Json Array 格式按某个字段转换成 {} 的格式,每一个 key = valuevalue 是单条记录集:{}

    ARRAY_PURE

    纯 Array 格式,对应 JavaScript 中类似 ["",""] 这种,和 ARRAY 不同点在于每个元素都是一个 纯值

    ARRAY_GROUP

    直接将一个 Json Array 格式按某个字段转换成 {} 的格式,每一个 key = value 中的 value 是一个 Array:[]

  • keyField 由于是多记录表格编辑器,用此属性用于指定按什么字段执行去重,此属性定义的是记录的 业务标识规则

表格编辑器如下:

0

列渲染器中一直存在 EDITORROW 两种不同的列处理流程,这是设计上的一种失误,正常来说表格编辑都应该是带 index(索引)的模式,但由于在 AntD 的干扰下,<Form/> 内的组件和自定义组件的 onChange 函数层面有很大的不同,新版已经逐步统一,但在早期 AntD 3.x 的版本中双向绑定的原因,使得表格内编辑出现了类似 onChange 函数签名不一致的情况,为了兼容这种情况,Zero Ui提供了 EDITORROW 两种列渲染器,针对不同的场景执行兼容型处理。这里未来是否可以统一还有待商榷,但目前引起的大部分表格编辑的 BUG 都是由于此处引起的,所以特此备注。

aiTransfer

内部封装了 AntD 中的 <Transfer/> 组件,但实际是自定义组件,用于设置左右穿梭框交互式操作,配置如下:

[
    {
        "metadata": "roles,用户角色,24,,aiTransfer",
        "optionJsx.config.datum": "source=ajax.roles,value=key,label=name",
        "optionJsx.config.valueKey": "key",
        "optionJsx.config.titles": [
            "待选择",
            "已选择"
        ]
    }
]

左右穿梭框如下:

0

特殊属性

配置属性表:

属性路径 含义

optionJsx.config.valueKey

当某些记录选中之后,使用记录中哪个属性的值作为最终的选中值:[v1, v2,…​]

optionJsx.config,titles

当前穿梭框的左右侧的标题设置。

aiTag (A)

对应 AntD 中的 <Radio.Group/> 组件变种,您可直接设置单选框信息,这种为特殊单选框,其配置如下:

[
    {
        "metadata": "color,标签颜色,,,aiTag",
        "optionJsx.config.items": [
            "geekblue,极客蓝",
            "magenta,粉红",
            "red,红色",
            "volcano,火山红",
            "orange,橙色",
            "gold,金色",
            "lime,青柠绿",
            "green,绿色",
            "cyan,青色",
            "blue,蓝色",
            "purple,紫色"
        ]
    }
]
选项说明

此处选项为特殊选项,是为 antd 量身打造的色彩选项,色彩只可以删除或维持,不可以追加,运行截图如下:

exp input tag

注意若使用的是英文模式,则需要在英文资源包中追加英文相关配置!目前版本只支持上述简单配置,后续可支持特定配置实现这种特殊效果。

aiTableTransfer

表格穿梭框,更为复杂的穿梭框组件,配置如下:

[
    {
        "metadata": "items,选择商品,24,,aiTableTransfer",
        "optionJsx.config.datum": "source=hotel.commodity,key=key,label=code",
        "optionJsx.config.tree": "text=:name(:code),parent=parentId",
        "optionJsx.config.filter": [
            "code",
            "name",
            "helpCode"
        ],
        "optionJsx.config.search": {
            "placeholder": "商品编码/名称/助记码"
        },
        "optionJsx.config.checkable": "leaf",
        "optionJsx.config.initialize": {
            "payTermId": "termId",
            "quantity": "NUMBER:1"
        },
        "optionJsx.config.table": {
            "columns": [
                {
                    "dataIndex": "key",
                    "title": "操作",
                    "$config": {
                        "text": "删除",
                        "confirm": "确认从选中项中移除该项目?"
                    }
                },
                "name,商品名称",
                {
                    "metadata": "payTermId,账单项,ROW",
                    "$config": {
                        "field": "aiTreeSelect",
                        "jsx": {
                            "style": {
                                "width": "160px"
                            },
                            "config": {
                                "datum": "source=term.all,key=key,label=name",
                                "tree": "text=label,parent=parentId",
                                "selection": "mode=LEAF,field=isLeaf"
                            }
                        }
                    }
                },
                "code,代码",
                "unit,单位",
                {
                    "metadata": "quantity,数量,ROW",
                    "$config": {
                        "field": "aiInputNumber",
                        "jsx": {
                            "min": 1
                        }
                    }
                },
                {
                    "metadata": "price,单价,CURRENCY"
                },
                {
                    "metadata": "amount,金额,TOTAL",
                    "$config": {
                        "currency": "¥",
                        "op": "M",
                        "field": [
                            "quantity",
                            "price"
                        ]
                    }
                },
                {
                    "metadata": "helpCode,助记码"
                }
            ]
        }
    }
]

表格穿梭框如下:

0

特殊属性

配置属性表:

属性路径 含义

optionJsx.config.datum

左侧构造树型结构的基础数据源,此处从字典数据中加载和读取。

optionJsx.config.tree

左侧树的构造配置,符合 Zero UI 中的标准树型配置结构。

optionJsx.config.filter

左侧过滤栏(可直接过滤树结构,主要是过滤子节点)。

optionJsx.config.search

左侧搜索框相关配置,<Input.Search/> 的所有属性都生效。

optionJsx.config.checkable

左侧树的选择模式,leaf 表示只能选择子节点。

optionJsx.config.initialize

从左侧 树记录 转换到右侧表格记录时的字段映射转换关系,每次初始化时的数据提取映射配置。

optionJsx.config.table

右侧列表配置,配置右侧的表格列等相关信息。

aiTreeSelect (A)

对应 AntD 中的 <TreeSelect/> 组件,树型下拉组件,一般数量不多的时候使用属性下拉比属性选择器合理。配置如:

[
    {
        "metadata": "termId,账单项目,,,aiTreeSelect",
        "optionJsx.config.datum": "source=term.all,key=key,label=code",
        "optionJsx.config.expr": ":name(:code)",
        "optionJsx.config.tree": "text=label,parent=parentId",
        "optionJsx.config.selection": "mode=LEAF,field=isLeaf",
        "optionJsx.config.linker": {
            "key": "termId",
            "name": "termName"
        },
        "optionConfig.rules": [
            "required,请选择合法的账单项!"
        ]
    }
]
特殊属性

配置属性表:

属性路径 含义

optionJsx.config.datum

当前树型下拉的数据源,从字典中提取。

optionJsx.config.expr

设置属性下拉的显示文字,支持前端的表达式方式,这种方式可更改下拉项的文字显示内容。

optionJsx.config.tree

树型下拉的树相关配置,符合 Zero UI中的树基础配置。

optionJsx.config.selection

Zero UI中的树选择模式设置,此处只能选择叶节点。

aiUserGruop

用户组专用选择器,账号管理 中专用,新版选择器,为用户选择用户组信息的专用组件。配置如:

[
    {
        "metadata": "groups,用户组,24,,aiUserGroup"
    }
]

用户组选择器如下:

exp input usergroup

aiUserLeader

经理选择器,专用用户经理选择,目前应用在 业务流程 中选择处理组相关信息,配置如下:

[
    {
        "metadata": "runDept,处理组,24,,aiUserLeader",
        "optionJsx.config": {
            "items": [
                "a,A组",
                "b,B组",
                "c,C组",
                "d,D组"
            ],
            "title": {
                "managerName": "组长",
                "workNumber": "组长工号",
                "mobile": "组长手机"
            }
        }
    }
]

组处理流程中,用户可以选择此处指定的 A、B、C、D 四个组,这四个组可选择多个而开启四个不同的子流程,由于组信息是存在于 optionJsx.config.items 配置中,所以您可以追加新的组构成五个组或其他,唯一需要说明的时若开启了多个组那么针对不同的组的流程图需独立绘制,此处的 UserLeader 属于一个综合性组件,此组件的应用依赖于 流程图。一般只有启用了 zero-wf 项目中的工作流引擎后此组件才容易被 重用,单独使用不是有效果。

它描述了这样一种场景,一个任务要分配到不同的组去完成,分配时根据实际情况先选择组以及组中的负责人,然后执行分配之后的任务流程。

aiUserSelector

用户选择器,内置封装了 ListSelector,和当前 Zero Extension 中的员工模块配合,实现用户信息的选择功能。配置如下:

[
    {
        "metadata": "acceptedName,当前处理人,8,,aiUserSelector,readOnly=true,placeholder=(谁将会审批该工单)",
        "optionJsx.config": {
            "linker": {
                "key": "acceptedBy",
                "realname": "acceptedName"
            },
            "user": "处理人"
        },
        "optionJsx.allowClear": true
    }
]

从上述配置可以知道选择配置比 ListSelector 大大简化,用户选择器如下:

0

此组件和 ListSelector 组件唯一的区别就是弹出框的内容差异,所以推荐使用此组件做 员工/人员选择

aiAction

前文已经带着开发人员了解了 Zero UI 中所有标准表单交互式组件,本章详细讲解提交按钮的配置,提交按钮在 Zero UI 中十分特殊,先看一段参考配置:

[
    {
        "metadata": "$button",
        "hidden": true,
        "optionJsx.extension": [
            "$opSave,保存,SUBMIT,primary",
            "$opDelete,删除,KEY",
            "$opReset,重置,RESET"
        ],
        "span": 24
    }
]

在上述配置中,您可以看到几个比较特殊的属性:

  • metadata / field`属性:由于历史原因,此处的值必须是一个固定值 `$button,只有这个值可以被 Zero UI 捕捉到并注入提交按钮的行为。

  • hidden 属性:在 属性解析器 章节您已经了解了 Zero UI 中的窗口连接模式:

    • 若此按钮不作为 连接点 按钮,而是直接作为可呈现的提交按钮对待,那么 hidden = false 或不设置。

    • 若此按钮作为 连接点 按钮对待,那么通常会设置 hidden = true 将按钮隐藏,若您想要查看防重复提交过程,可以考虑此处 hidden = false 做调试。

  • span 属性:如果按钮隐藏,此处设置什么值都无碍。

参考如下片段体会两种配置的区别(个人密码修改页)

[
    {
        "metadata": "$button,,14",
        "optionJsx.extension": [
            "$opPassword,更改密码,SUBMIT,primary",
            "$opReset,重置,RESET"
        ]
    }
]

由于按钮部分有可能存在多个按钮,所以第二占位符 optionItem.label 配置没有任何实际作用。

按钮配置

提交按钮在表单的骨架代码中一般如下:

    <ExForm {...form} $height={"300px"} $op={Op.actions}/>

操作部分代码如:

import Ex from 'ex';
import Ux from "ux";

const $opPassword = (reference) => (params) => {
    const user = Ux.isLogged();
    const opassword=Ux.encryptMD5( params['opassword']);
    if(opassword !== user.password){
        return Ux.ajaxDialog(reference, {
            key: "passwordError", /* 专用窗口对应的key */
        });
    }
    const request = Ux.valueRequest(params);
    request.password = params['npassword'];
    return Ex.I.password(request)/* 更新密码回调处理 */
        .then(data => Ux.ajaxDialog(reference, {
            data, /* 响应数据 */
            key: "password", /* 专用窗口对应的key */
        }))
        /* closed 参数这里不显示 */
        .then(() => Ex.Op.$opLogout(reference));
};
export default {
    actions: {
        $opPassword
    }
}

注意此处的配置行:

"$opPassword,更改密码,SUBMIT,primary"

这些内容对应的按钮配置如下:

索引 属性名 含义

0

key

如果作为 连接点,此处的 key 还可以作为客户端 id 直接被连接(隐藏按钮时的触发点),若作为直接按钮则表示按钮的 key

1

text

按钮中的文字,此文字会作为 <Button>xxxx</Button> 显示在按钮上,若 hidden = true 时可直接忽略。

2

event

事件名称,事件名称在按钮中属于重点配置

  • 基于服务端配置时( zero-ui ),event 可作为事件名来挂载标准化行为。

  • 客户端开发时,event 直接指定事件名和相关行为来挂载对应的提交行为。

3

type

按钮类型,对应 AntD 中的 <Button/> 按钮的类型,如:primary, default, denger

4

className

按钮的 CSS 风格设置(可自定义风格,如 ux_red, ux_green 这种全局色)。

5

icon

按钮上的图标,可支持 图标表达式

6

$KV$

原子解析属性 部分,您可以直接设置扩展属性。

带回调配置

表单的回调配置在早期的版本中都是位于 UI.json 中的根节点 _modal 中,而新版本中的窗口函数可以直接配置在 _form 内部实现内部的 闭合 回调。参考流程配置表单如下:

{
    "_form": {
        "modal": {
            "success": {
                "open": "您好,您的流程申请已成功提交,申请单号 :serial,请等待审批。",
                "draft": "您好,您的流程申请已成功保存为草稿!单号为 :serial。"
            }
        },
        "ui": [
            [
                {
                    "metadata": "$button",
                    "optionJsx.grouped": true,
                    "optionItem.label": "流程操作",
                    "optionJsx.extension": [
                        "$opStart,提交,SUBMIT,primary,,upload,closable=true,callback=open",
                        "$opDraft,暂存,SUBMIT,,uc_pink,,closable=true,callback=draft",
                        "$opReset,重置,RESET",
                        "$opBack,返回,BACK"
                    ],
                    "span": 24
                }
            ]
        ]
    }
}

上述按钮配置中,您可以在 $KV$ 部分看到类似 closable=true,callback=open原子解析配置 扩展,这种扩展会自动在回调完成之后执行自动化行为(零代码模式)。

  • closable

    • 如果这个值为 true,则提交之后会调用 rxClose 函数触发关闭行为,这种关闭包括:关闭弹窗、关闭页签、关闭抽屉窗 等。

    • 如果这个值为 false,不做任何关闭操作,仅执行 表单重置

  • 上述示例中回调最终配置如下,按钮在提交之后会自动触发回调并完成 表单操作 闭环:

    按钮ID 事件 回调键 配置路径

    $opStart

    SUBMIT

    open

    modal/success/open

    $opDraft

    SUBMIT

    draft

    modal/success/draft

还有一种格式是最新版才追加的,为了避免手工设置 api 专用(必须使用 ai sync 更新到 最新版),参考如下:

{
    "metadata": "$button",
    "hidden": true,
    "optionJsx.extension": [
        "$opAdd,添加,SUBMIT,primary,,,closable=true,callback=added,api=/api/nm-law",
        "$opReset,重置,RESET"
    ],
    "span": 24
}

这种格式是 最新版 的 Zero Ui 支持的格式,主要目的是通过配置完成所有的表单级操作,但是此处的 api 有个限制,HTTP 方法是固定的:

  • 添加 - POST

  • 保存 - PUT

  • 删除 - DELETE

这样配置之后,在 smartList 这个级别就可以省略如下配置:

{
    Form: {
        name: "FormLaw"
        /*
            旧版本中必须提供 A / S / D 这三个值才能驱动发送远程请求
            A - Add - POST方法
            S - Save - PUT方法
            D - Delete - DELETE方法
            新版本可以配在 `$button` 中实现
        yoOp: {
            A: "/api/mn-law",
            S: "/api/mn-law/:key",
            D: "/api/mn-law/:key"
        }
        */
    }
}
关于 event

按钮事件主要分为两部分:服务端(配置型)和 客户端(开发型)。

服务端事件
事件值 含义

event.filter

过滤表单专用提交事件,提交的数据一般作为查询条件,使用在 高级搜索 中。

event.add

添加表单专用事件。

event.edit

编辑表单保存时专用事件。

event.delete

编辑表单删除时专用事件。

客户端事件
事件值 含义

SUBMIT

标准的表单提交,一般在事件函数中开发 Ajax 远程接口请求。

SUBMIT_REDUX

(旧模式)触发 Redux 提交流程,回调之前和之后会触发 Redux 的防重复提交流程。

SUBMIT_DIALOG

窗口表单提交,在弹出窗的 表单 中触发提交,执行完成之后关闭弹窗。

SAVE_ROW

此事件一般用于子表单提交,类似 DialogEditor 中添加子记录时专用做法,提交完成之后会保存(添加/更新)子记录相关信息。

BACK

「不需函数」返回专用,触发 react-router 中的返回页面专用函数。

RESET

「不需函数」重置表单专用,提交完成之后重置表单。

KEY

只带记录 key / id 的表单提交,如 删除(只提供 key 就可以实现记录的删除)。

aiTitle

标题栏的定义方式和其他交互式控件有所区别,所以此处并没有类似 aiXxx 的函数对其进行渲染,此处介绍一下标题的渲染配置。

直接标题

参考如下配置:

[
    "title=组基本信息"
]

这是最简单的标题行配置,直接会呈现成如下:

0

模块化标题

参考如下配置:

[
    "subject=HOTEL-ORDER"
]

模块化配置结果如下:

0

这种配置位于 模块化参数 过程中,属于特殊标题栏配置,React 组件会从上层 props 中提取变量 $subject 变量中的配置信息,并生成截图中对应的标题栏,此标题栏会对应到后端 B_BAG / B_BLOCK 的结构中,所有配置数据都和表结构中存储的配置数据相关。

提醒文字

参考如下配置:

[
    {
        "title": "房态说明",
        "config": {
            "description": [
                "停用房为最高优先级,一旦停用其他状态不可随意变更。",
                "只有`可用房`会参与到主流程中,其他类型的房间在主流程中均不可用。",
                "清洁状态可独立更新,辅助房务中心做房态调整。",
                "`预离`和`预抵`房为特殊房态,且系统自动计算,不可维护,只可查看。"
            ]
        }
    }
]

这种配置的效果如下:

0

此配置对应的是 AntD 中的 <Alert/> 组件,所有属性都遵循它对应的信息,包括 message, description, type 等,您可以按自己的需要配置。 :data-uri: :table-caption!:

aiMagic

数据呈现器,此组件专用于 非交互型 数据呈现,配置如:

[
    {
        "metadata": "method,HTTP方法,4,,aiMagic",
        "optionJsx.config.items": [
            "GET,GET:calendar,16:#228B22",
            "PUT,PUT:calendar,16:#228B22",
            "POST,POST:calendar,16:#228B22",
            "DELETE,DELETE:thunderbolt,16:#D02090"
        ]
    }
]

上述配置会捕捉对应数值,并生成 选项 呈现。

选项配置

选项配置一般位于 optionJsx.config.items 中,你可以设置两种配置:

  • 原始配置,不带图标的基础配置:

    "GET,GET方法"
  • 扩展配置,带图标的基础配置:

    # 由于历史原因,此处的选项配置格式不要写错
    "GET,GET方法:calendar,16:#228B22"

上述配置中几个值含义如:

含义

GET

选项的值,对应 key / value 属性。

GET方法

选项的显示文本,对应 label 属性。

calendar

图标类型,对应 <Icon type="calendar"/>(旧版本写法,新版有API做兼容,所以 type 依旧生效)。

16

图标的尺寸,对应 size 属性。

#228B22

图标的颜色,对应 style.color 属性。

呈现器种类
  1. 列表呈现器,如果 value 值是 Array 类型则自动触发列表呈现器。

    配置项 含义

    optionJsx.config.vertical

    显示成水平列表还是垂直列表。

    列表中的项支持递归,即内置的 <li></li> 的呈现部分依旧可以是嵌套类型的 aiMagic 视图。

  2. 时间呈现器,如果 value 的值是时间格式 Ux.isMoment 检查返回 true,则做时间格式呈现。

    配置项 含义

    optionJsx.config.format

    时间格式化专用的模式字符串,如 YYYY-MM-DD

    {
        "metadata": "updatedAt,最后更新,,,aiMagic",
        "optionJsx.config.format": "YYYY年MM月DD日 HH:mm",
        "moment": true
    }
  3. 选项呈现器,若配置中包含了 items 的配置,则执行选项呈现器。

    配置项 含义

    optionJsx.config.items

    当前环境可支持的选项数据,参考最早的示例。

    optionJsx.config.datum

    如果选项使用的不是 静态数据 而是动态字典,则可支持直接从动态字典中提取数据。

  4. 记录呈现器,若配置中包含了 record 配置,则执行记录呈现。

    记录解析之后会包含多种呈现模式,通常是采用 JSON 的方式直接呈现记录信息,若记录中带有解析的前缀 prefix,则可以考虑设置前缀而实现记录的 标题 部分。

    {
        "metadata": "infoReadable,详细内容,19,,aiMagic",
        "optionJsx.config.record": true
    }
  5. 用户呈现器,若配置中包含了 user 配置,内置呈现和列渲染 USER 一致,所以其配置格式也是一致的。

    {
        "metadata": "updatedBy,更新人,,,aiMagic",
        "optionJsx.config.user": {
            "uri": "/api/user/:key",
            "field": "realname"
        },
        "optionJsx.$empty": "(系统)"
    }
  6. 下载文件呈现器,若配置中包含了 download 配置则采用下载链接呈现器。

    {
        "metadata": "fileUrl,链接地址,24,,aiMagic",
        "optionJsx.config.download": {
            "text": "下载文件",
            "filename": "name"
        },
        "optionJsx.config.preview": {
            "type": "type",
            "empty": "(该版本暂不提供预览功能)"
        }
    }
  7. 表格呈现器,若配置中包含了 table 配置,内置使用表格呈现器(列表配置)。

  8. 带单位文本

    {
        "metadata": "amount,退款金额,,,aiMagic",
        "optionJsx.config.currency": "¥"
    }

除了上图 呈现器 以外,其实呈现器还拥有其他特殊的配置,开发人员可直接参考源代码去了解,文档中的呈现器只是枚举了常用的。

aiSearchInput

Qr类型的组件只有两个自定义的比较特殊的组件,其他组件的用法可直接参考现有的 查询表单 来完善,如 Qr 在输入表单上的更改类似:

  • 字段名一般会使用 ` 符号做分割(符号之后是查询操作符),配置如:

    "name`c,名称,24"
  • 如果是下拉类型的则多数都支持多选模式,包括 aiRadio / aiCheckbox / aiSelect 的选项都是支持多选模式的,最终使用 IN 操作符。

此交互式组件为搜索专用的组件,一般在搜索表单中使用。配置如:

{
    "metadata":"contactName,联系人姓名,12,,aiSearchInput",
    "optionJsx.layout": {
        "left": 14,
        "right": 10
    }
}

该查询组件如下:

0

aiSearchRangeDate

查询范围组件,配置如:

{
    "metadata":"infoAt,日志记录时间,24,,aiSearchRangeDate",
    "optionJsx.config":{
        "mode":"FULL",
        "format":"YYYY-MM-DD HH:mm",
        "placeholder":["开始时间","结束时间"]
    }
}

查询范围组件一般用于 时间格式 的查询过程中,此处就不截图了。

模式
含义

FULL

日期时间全格式

DATE

日期范围查询

MONTH

月份范围查询

TIME

时间范围查询

验证规则

验证规则主要针对 optionConfig.rules 的配置部分的配置生效,您可以直接配置各种有用的验证器。参考配置:

{
    "optionConfig.rules": [
        "required,请输入仓库名称,名称不可为空!"
    ]
}

上述配置是最常用的配置,此处的规则中也可以直接使用 AntD 的默认验证器配置,由于默认验证器配置在官方文档中存在,所以就不在这里重复,本章主要讲解部分常用的 自定义验证器

下边验证器都是自定义扩展验证器,所以标题中的值等价于 validator=<title>

existing

存在性验证专用配置(高频验证器)。参考配置:

{
    "validator": "existing",
    "message": "对不起,支行名称重复!",
    "config": {
        "uri": "/api/bank/existing",
        "method": "POST",
        "params": {
            "sigma": "PROP:app.sigma"
        }
    }
}

假设有表结构

属性 数据库列

key

KEY

code

CODE

docId

DOC_ID

此处配置可追加一个特殊配置 alias 用于对字段属性进行 映射 配置,在存在性检查的编辑界面中,检查时要移除掉当前记录的存在,简单说若您系统中存在一个 A 的属性为 code(CODE) 的记录,那么此处应该在SQL中您应该追加:

KEY <> '<key>'

上述表达式中 <key> 中为 主键排除 模式,这种存在性检查只在编辑模式中生效以防止编辑和添加的存在性检查逻辑,简单说:

  • 添加记录时,若系统中存在 A 的记录,则无法添加。

  • 编辑记录中,若系统中存在 A 的记录且主键和当前数据记录主键一致,则表示同一条记录,依旧允许更新。

alias 则用于配置条件中的左边属性,示例:

{
    "validator": "existing",
    "message": "对不起,法规编号重复!",
    "config": {
        "uri": "/api/doc/existing",
        "method": "POST",
        "params": {
            "sigma": "PROP:app.sigma"
        },
        "alias": {
            "key": "docId"
        }
    }
}

上述属性 alias.key 是主键固定值,之所以是对象模式是为了考虑后期复合主键(多个字段做主键)的情况,上述结构会生成下边SQL语句:

DOC_ID <> '<key>'

来执行编辑过程中存在性检查的当前记录排除,默认情况若主键本身是 key 属性,则可以不用配置(大量场景可不用配置)。

uri

远程延迟验证,更广义的 Ajax 验证,可实现各种 不可思议 的验证方式,开发人员阅读源代码自行理解。

filename

合法文件名验证。参考配置:

{
    "validator": "filename",
    "message": "对不起,目录名包含特殊字符,请检查!"
}

required

必填验证,此验证器和最早示例中的默认 required 有所区别,此验证器属于增强型,可检查 ArrayObject 等复杂数据结构。

{
    "validator": "required",
    "message": "对不起,订单项不可为空,请添加订单项!"
}

mix

最小值验证器(增强型)。参考配置:

{
    "validator": "min",
    "config": {
        "min": 0
    },
    "message": "早餐券不能为负数!"
}

max

最大值验证器(增强型)。

range

范围验证器。参考配置:

{
    "validator": "range",
    "config": {
        "min": 0,
        "max": 8
    },
    "message": "超预订数量只能在合理的 0 ~ 8 的范围!"
}

currency

货币验证器。参考配置:

{
    "validator": "currency",
    "message": "对不起,价格不能为负"
}

after

在目标时间之后

{
    "validator": "after",
    "message": "离店时间必须晚于到店时间!",
    "config": {
        "to": "arriveTime"
    }
}

before

在目标时间之前

less

增强型小于某值

{
    "validator": "less",
    "message": "拆分金额必须小于原始金额!",
    "config": {
        "to": "amount"
    }
}

lessOr

增强型小于等于某值

great

增强型大于某值

greatOr

增强型大于等于某值

same / equal

相同值验证

{
    "validator": "same",
    "message": "两次输入密码不一致!",
    "config": {
        "to": "npassword"
    },
    "condition": [
        "to.length >= 8"
    ]
}

diff

不同值验证

列表配置

列表开发

一般在一个完整的系统开发中,最高频的两种中后台管理页面就是:表单页和列表页,前文已讲解了表单页中的基本配置和组件用法,本章节开始带开发人员看看 Zero Extension 中提供的列表组件。

  • ExListFast:快速列表

  • ExListComplex:标准列表

可以说 ExListComplex 组件几乎是为后端的 zero-crud 量身打造的全功能模块,此列表支持 15 个标准接口,Zero Extension 中的支持的接口并非传统的 增、删、查、改 四个维度,追加了实战过程中常用的扩展维度(包括视图、高级搜索),从实践经验可以知道这种 复杂列表 在企业应用中才可支撑大量产品化、快速交付项目的基本需求,特别针对中后台管理页达到不错的管理效果。

本篇先不结合截图讲解列表页选项(此部分直接在 实战模块 中加以详细说明),先简单讲解一下 Zero Extension 中列表的基础知识,截图由零点科技有限公司(使用Zero的友人)提供。

功能表

Zero Extension 中列表的功能支持如下:

  • 访问 /api/xxx/search 查询分析引擎接口,实现列表页的基本呈现,包括:分页、排序、列过滤、跳页、查询

  • ExListComplex 组件的页面流如下:

    0

    -

待完善的部分:

  • ExListComplex 目前的入口视图是固定的,view = DEFAULT,后期需要打造个人设置将某个模块的个人入口设置到单独的视图上实现个性化入口定制。

  • 批量编辑现阶段只提供了 配置模式 的简易编辑和 开发模式 的复杂表单编辑功能,后期需提供 开发模式 的表单编辑功能实现增强批量处理。

  • 部分细节现阶段支持的组件数量有限,后期待补齐,包括

    • 列过滤种类

    • 查询条件种类

    • 列渲染种类

  • 目前的附加功能局限于:导入、导出、列筛选、视图管理,后续此处的行为需进一步添加。

  • ExListComplex现阶段是不支持 查看页 的,从图上可以知道,mode 只支持 ADD / EDIT 两种,后续需补齐查看页做标准化管理。

工作区域

0

截图中区域说明(区域标识以截图中蓝色文字为主):

区域标识 含义

open

添加操作区域,默认只带有 添加 按钮可直接打开 mode=ADD 的页签。

batch

批量更新、批量删除区域,此处可扩展更多带有 选择模式 的批量行为,可直接扩展 op.extension 选项实现。

search

搜索统一区域:设置查询条件、高级搜索表单、清空查询条件 等。

extra

右上附加区域,导入、导出、列筛选、试图管理 等。

column

列表头部区域,列过滤、排序 等。

pagination

分页器,支持 分页、页尺寸设置、跳页 等常用分页功能。

配置概览

本文主要解析传入 ExListComplex 中的 config 属性的核心数据结构,让开发人员可以根据自己实际需要对列表进行扩展和定制。

UI.json 的基本结构如下:

{
    "_page": {

    },
    "_assist": {

    },
    "_grid": {
        "query": {},
        "options": {},
        "component": {},
        "table": {}
    }
}

上述结构是开发 列表页 的专用配置结构,配置项内容如下:

根节点 子节点 含义

_page

此节点一般用于绑定 <PageCard/> 实现标题和外层容器的开发。

_assist

辅助数据开发,等价于表单内页的 assist 部分,辅助数据会提前在您操作之前从远程直接拉取 辅助数据(字典数据、外联数据、自定义数据、分类数据)等。

_grid

query

当前列表组件默认查询条件,第一次查询依赖此 查询条件 读取远程列表数据。

options

选项数据,您可以更改此配置对 列表组件 进行各种设置。

component

常用扩展组件:批量编辑表单、导入表单、导出表单、视图管理、列筛选表单 等几个组件的相关配置都可以直接在此处设置。

table

表格组件,主要包含了 列渲染器,此处有一点需说明:

  • 若您的列表使用的是 静态列配置,那么您可以直接在 table.columns 中执行完整定义。

  • 若您的列表使用的是 动态配置dynamic.column = true ),此时列相关信息依赖后端的列读取,table.columns 中只提供操作符列相关定义即可。

标准开发

标准方法开发中,你可以在页面这一级直接读取完整结构的列表配置,并将配置以 config 的属性传入 ExListComplex 组件中。参考如下代码:

import React from 'react';
import Ux from 'ux';
import FormAdd from './form/UI.Add';
import FormEdit from './form/UI.Edit';
import FormFilter from './form/UI.Filter';
import Ex from "ex";
import {ExListComplex} from "ei";
import Op from "./form/Op";
import {PageCard} from "web";

@Ux.zero(Ux.rxEtat(require("./Cab"))
    .cab("UI")
    .to()
)
class Component extends React.PureComponent {
    state = {
        $ready: false
    };

    componentDidMount() {
        Ex.yiStandard(this).then(Ux.pipe(this));
    }

    render() {
        return Ex.yoRender(this, () => {

            const config = Ux.fromHoc(this, "grid");
            /* 专用组件信息 */
            const form = {
                FormAdd,    // 添加表单
                FormEdit,   // 更新表单
                FormFilter  // 搜索表单
            };
            return (
                <PageCard reference={this}>
                    <ExListComplex {...Ex.yoAmbient(this)}
                                   rxPostDelete={Op.rxPostDelete(this)}
                                   rxAssist={Op.rxAssist(this)}
                                   config={config} $form={form}/>
                </PageCard>
            )
        }, Ex.parserOfColor("PxRBACGroup").page());
    }
}

export default Component

上述代码是 Zero Extension 中用户组的管理入口页,如果使用这种方式开发列表入口页,注意如下几点:

  • 外层组件可以直接使用 <div/> 标签组件,你也可以按照示例中直接使用 <PageCard/> 组件(带有头、标题、按钮和右上 Extra部分的基础页面容器)。

  • componentDidMount 中的 Ex.yiStandard 方法调用是 可选的,它取决于您的列表页中是否带有初始化数据部分 _assist 辅助数据,这些辅助数据可直接作用于 列表、表单 作为辅助字典数据来处理。

  • 绑定资源文件之后,配置部分从 UI.json 中的 _grid 中提取全列表配置。

  • 此处依赖三个表单文件,且表单键值是固定的:

    表单键 含义

    FormAdd

    添加表单,mode=ADD 的表单组件。

    FormEdit

    更新表单,mode=EDIT 的表单组件。

    FormFilter

    高级搜索表单,直接提供高级搜索专用表单定制功能,您可定制不同的查询条件。

    FormBatch

    (保留)后期拓展批量编辑中的自定义部分设置批量编辑专用表单。

  • 示例代码中的 rxPostDelete / rxAssist 不属于标准部分的代码,它属于定制部分,所以标准部分代码只需要实现如下功能即可:

    属性 含义

    Ex.yoAmbient(this)

    Zero 扩展框架中的核心继承方法,可以帮助您针对上下文属性实现向下继承的功能,其中包括:

    • 常用的辅助数据( $a_ )和字典数据( $t_ )。

    • 常用的上层函数,函数前缀( rx / fn / ix )。

    • 全局数据:应用 $app、登录账号 $user、路由 $router 等。

    config

    读取到的 ExListComplex 的完整配置数据,从 UI.json_grid 节点提取。

    form

    传入 ExListComplex 表单完整数据。

这种开发模式比 快速开发 模式多开发完整的 表单 页(三个表单页),不仅如此表单页所有行为函数都需开发人员提供( Op.js ),所以这种开发模式可定义为 开发型;标准开发

快速开发

快速开发依赖新库 ui 中提供的智能化开发模块。

Ui.smartList

smartList 的骨架代码如下:

import Ui from "ui";
import Ex from 'ex';
import Ux from 'ux';

export default Ui.smartList({
    ns: require("./Cab.json"),
    name: "Integration.SMS",
    logger: "toolkit",
    Options: {
        rm: [
            "form.filter",      // 关闭高级搜索表单
            "op.extra.export",  // 按钮:导出
            "op.extra.import",  // 按钮:导入
            "op.batch.delete",  // 按钮:批量删除
            "op.batch.edit",    // 按钮:批量编辑
        ]
    },
    Form: {
        name: "FormSms",
        yoOp: {
            A: "/api/i-integration",
            S: "/api/i-integration/:key",
            D: "/api/i-integration/:key"
        }
    },
    componentInit: (reference) => {
        Ex.yiAssist(reference, {})
            .then(Ux.ready).then(Ux.pipe(reference))
    }
})

快速开发模式主要调用 Ui.smartList 的API完成页面的综合性定制,此处针对 Ui.smartList 做个简易的说明,这个函数只有一个参数:

const smartList = (configuration = {}) => {
    // ...
}

configuration 的配置项数据结构如下:

基础配置
配置项 含义

ns

关联名空间连接文件,此处必须调用 require 方法连接名空间。

name

这个是页面名称,您可以随意设置页面名称,在开发过程中此名称会出现在浏览器的开发工具 console 中,您可以精确定位日志是从什么地方出来的。

logger

可使用的日志器,日志器必须是合法日志器,参考 日志器 章节。

Cab

(驼峰命名),若不指定此选项,那么当前页面会直接绑定到 名空间UI.json 文件中,若您要指定就必须指定 Cab 对象中 page 属性(不带 .json 后缀)。

Options

(驼峰命名),您可以设置 rm 属性关闭列表页中不使用的选项,选项参考下一章节单独说明。

Form

(驼峰命名),此处的 Form 属性对应 Ui.smartForm 中的配置信息,常用四个属性如:

  • name:表单的名称

  • yoOp:当前操作中的常用 RESTful 地址

    • A:添加接口(POST方法)

    • S:保存接口(PUT方法)

    • D:删除接口(DELETE方法)

生命周期函数
配置项 含义

componentInit

对应 React 中的 componentDidMount 生命周期。

componentUp

对应 React 中的 componentDidUpdate 生命周期。

componentYo

对应 React 中的 render 生命周期。

Extension扩展
配置项 含义

yoOp

扩展属性 $op,对操作按钮执行变更实现按钮扩展。

yoExecutor

扩展属性 $executor,主要针对横行中的操作,如 编辑、删除、权限设置 等,列表行操作必须。

yoPlugins

扩展属性 $plugins,针对 Zero Extension 组件渲染支持的插件进行定义。

yoRenders

扩展属性 $renders,自定义 表单渲染器,对应 <ExForm/> 中的 $renders 实现表单交互式组件的自定义。

yoRx

扩展属性 rx 前缀类型的函数继承。

renderAddOn

列表之下的 附加渲染器,可渲染额外区域的相关组件。

Ui.smartForm

smartForm 实际是快速绑定表单专用资源文件实现表单级的快速开发模式,这种场景下不需要您提供额外的表单代码文件,外层若调用 smartList,那么此处的 Form 配置中的每一种表单配置都会触发一次 smartForm 实现标准模式下三种表单的快速配置流程。

此函数签名如:

const smartForm = (configurationForm = {}, mode) => {
    // ....
}
基础配置
ns 关联名空间连接文件,此处必须调用 require 方法连接名空间。

name

这个是表单名称,您可以随意设置表单名称,在开发过程中此名称会出现在浏览器的开发工具 console 中。

Cab

(驼峰命名),默认会使用标准化代码针对 FormAdd, FormEdit, FormFilter 三种表单绑定不同的资源文件,默认绑定 UI.Add.json、UI.Edit.json,UI.Filter.json

生命周期函数
配置项 含义

componentInit

对应 React 中的 componentDidMount 生命周期。

componentUp

对应 React 中的 componentDidUpdate 生命周期。

componentYo

对应 React 中的 render 生命周期。

componentValue

表单特殊的生命周期执行,在 render 中再次执行初始化值的相关计算流程。

Extension扩展
配置项 含义

yoOp

扩展属性 $op,表单提交专用函数(直接和 aiAction 绑定的按钮函数,一般是二阶函数)。

yoJsx

扩展属性 $renders,表单自定义渲染器,若表单中的组件依赖 用户自定义组件,可直接使用此属性扩展。

yoAcl

扩展属性 $edition,针对表单执行 ACL 的权限控制 自定义

yoPlugins

扩展属性 $plugins,针对表单部分插件配置,可挂载外置插件。

上述提供的所有 yo 类型的扩展属性都包含两种形态:

  • Funcation 形态,直接执行过后产生所需的配置项 {}

  • Object 形态,直接将此配置项作为 扩展点 返回。

快速配置

若您使用了 Ui.smartXxx 的方式做 列表表单 的快速开发,那么配置文件也会大大简化(达到了快速开发的目的)。参考配置如:

{
    "_assist": {
        "zero.integration": {
            "uri": "/api/type/tabulars/:type",
            "magic": {
                "type": "FIX:zero.integration"
            }
        }
    },
    "_grid": {
        "module": {
            "NAME": "邮件配置",
            "MODULE": "i-integration",
            "IDENTIFIER": "i.integration"
        },
        "query": {
            "criteria": {
                "sigma": "PROP:app.sigma",
                "type": "email",
                "": "OPERATOR:AND"
            }
        },
        "options": {
            "tabs.title": "Email服务器配置",
            "tabs.container": true,
            "search.advanced": false
        }
    }
}

上述配置文件需说明:

  • _assist 部分依旧,用于提取辅助数据,不仅如此,若要加载辅助数据您还需在 componentInit 中手工书写代码(类似 componentDidMount 函数)。

  • module 参数,此参数用于模块鉴别,参数含义如下:

    参数名 含义

    NAME

    呈现当前模块的显示主体模型文字。

    MODULE

    对应 crud 模型中的 name,构造标准化模块时 :actor 部分专用。

    IDENTIFIER

    当前管理模块的模型标识符。

  • query.criteria 参数,直接书写当前页面的 Qr 查询参数。

  • options 参数,若您不想使用默认值,则可以直接在此处更改 options 配置参数对列表进行修订。 :data-uri: :table-caption!:

Option选项配置

选项:搜索类

搜索类选项设置都采用 search 作为前缀,定义了列表的如下功能:

  • 基础搜索

  • 高级搜索

  • 视图管理(查询条件部分)

搜索类参考配置如下:

{
    "search.cond": [
        "name,c",
        "code,c"
    ],
    "search.enabled": true,
    "search.confirm.clear": "该操作会清空所有的查询条件,确认清空?",
    "search.op.redo": "清除条件",
    "search.op.advanced": "高级搜索",
    "search.op.view": "查询条件",
    "search.placeholder": "名称/编码",
    "search.advanced": true,
    "search.advanced.width": "40%",
    "search.advanced.title": "搜索用户组",
    "search.advanced.notice": {
        "message": "注意!",
        "description": [
            "高级搜索条件优先级会大于普通搜索,一旦触发,普通搜索条件会被重置。",
            "高级搜索表单中的搜索条件会被保存,再次打开时会重置到上次的搜索状态。"
        ]
    },
    "search.criteria.window": "当前查询条件,保存,关闭,false,900,true,btnCriteria",
    "search.criteria.view": {
        "selected": "当前视图:",
        "confirm": "您正在更改当前视图的查询条件,一旦更改后查询数据会有变化,确认?"
    }
}
search.cond

列表主页搜索框中的查询命中条件,此处查询命中条件会构造成 OR 的方式执行查询,如示例中会生成SQL语句:

-- NAME / name,  CODE / code
WHERE NAME LIKE '%xx%' OR CODE LIKE '%xx%'
search.enabled

基础搜索框的开关,若此处 search.enabled = false 则自动隐藏列表头部的搜索框。

search.confirm.clear

exp opt search.confirm.clear

点击清除查询条件按钮过程中,此选项可直接设置弹出框的呈现文字。

search.op.redo

是否呈现搜索框右侧的 清除条件 的按钮,若存在该选项则显示,且值为鼠标移上去过后的浮游文字。

search.op.advanced

设置搜索框右侧的 高级搜索 的按钮文字,高级搜索会使用另外一个配置来指定 开/关,所以高级搜索功能打开时若没有设置当前选项只是不显示浮游文字,但高级搜索功能依旧可用。

search.op.view

是否呈现搜索框右侧的 查询条件 的按钮,若存在此选项则显示,其值为鼠标移到按钮上的浮游文字。

search.placeholder

基础搜索框上的水印文字设置,一般此属性和 search.cond 配合设置。

exp opt search.placeholder

search.advanced

search.enabled 类似,此按钮是高级搜索的开关,您可以设置此选项开启或关闭高级搜索。

search.advanced.width

高级搜索是在右边以抽屉的方式弹出界面,抽屉窗口中是一个 查询条件设置表单FormFilter),此属性设置窗口位于页面中的宽度,若您的查询条件比较多可以考虑将此值设置得更大。

search.advanced.title

抽屉窗口顶部的标题文字,您可以自定义呈现的文字信息。

search.advanced.notice

抽屉窗口上半部分的提示文字,用于提示用户高级搜索表单的基本用法。

0

search.criteria.notice

查询条件 弹框顶部可设置提示消息,此提示消息可以显示在弹框顶部,和高级搜索提示文字格式一致。

search.criteria.window

若开启了 查询条件 设置的功能,此属性指定了查询条件弹出窗口的相关配置,内置使用了 组件解析器 解析该窗口。

search.criteria.view

查询条件 功能可以对当前选择视图进行 视图编辑,这种模式下,此组件对应的文字使用当前选项执行配置,此处的配置主要针对过场效果文字进行定义。

0

search.grid

列表模板选择,您可以按照您所喜好设置对应的 grid 信息,系统有默认值存在。

选项:页签部分

页签配置主要是针对 <Tab/> 类组件,此处设置相对比较简单,支持两种模式:

  • 若外层页带了 <PageCard/> 做主容器,此种场景下页签可使用 标准模式 配置。

  • 若外层页没有使用任何容器组件,页面可以作为独立容器来使用,这种场景下您依旧可以设置顶部的标题。

页签部分选项参考配置:

{
    "tabs.title": "FTP配置",
    "tabs.container": true,
    "tabs.list": "FTP配置列表",
    "tabs.add": "添加FTP配置",
    "tabs.edit": "编辑FTP配置"
}
tabs.container

此选项用于切换当前页签的模式:标准模式 / 独立容器模式,若值为 true,那么页签容器可以自带标题信息,而忽略外层容器对象。

tabs.title

当页签使用 独立容器 模式时,您可以通过此选项指定页签的 标题 文字。

tabs.list

列表页页签头文字。

tabs.add

列表打开添加页时( mode=ADD ),页签头文字。

tabs.edit

列表打开编辑页时( mode=EDIT ),页签头文字。

tabs.type

对应 <Tab/> 组件的类型,直接绑定到 AntD 的原生属性中。

tabs.count

当前页面可以打开的最大数量,设置数量之后,您不可以随意打开多余的页签。

tabs.disabled

是否开启 单列表 功能,当前 ExListComplex 列表页是开启了 单列表 功能的,效果如下:

exp opt tabs.diabled

当你打开了 添加页/编辑页 时,第一个列表页的 页签 目前是被禁用的,只有关闭打开页签之后才可以回到列表中操作。

此处这种 集中 处理的设计有助于使用者将精力集中在当前界面应该考虑的部分,而不至于分心去不同页签上操作不同单据,很多大企业的此部分没有做这样的改动,用户可以无限打开页签,事实证明很多错误信息的填写都是出现在页签和页签进行切换过程中,企业数字化流程中,Zero 推荐用户的 单一职责 模式处理业务,若您现在正在处理单个工单,那么您就集中精力处理此工单,而不要分心做其他事。

tabs.extra.add

添加页右上角 Extra 的内容配置,可注入行为对容器右上角进行特殊的定制。

tabs.extra.edit

编辑页右上角 Extra 的内容配置,可注入行为对容器右上角进行特殊的定制。

选项:视图/模块

模块和视图选择和安全视图在列表层处理有关,Zero Framework中的视图处理如下:

视图参数 含义

view

当前接口位于的视图信息。

position

当前接口位于的位置信息,位置信息位于视图信息的高阶,您可以先指定位置,再指定视图。

而此处的模块和视图的参数命名规则本身未统一,但位于不同的分类之下,由于这些参数定义的内容一致,所以放到一起来讲解。

{
    "identifier": "datum.room-rent",
    "ajax.module": false,
    "ajax.position": [
        "ROUTE:type"
    ]
}
identifier

当前列表绑定的后端模型统一标识符,此属性很重要,动态建模过程中甚至会影响到模型的形态问题,按 Zero Extension 部分的整体规划,模型后期可通过 identifier 从服务端的模型接口拉取当前模型相关的所有定义,而每个列表都会绑定一个 主模型

ajax.module

若系统做了多表连接( JOIN 模式),使用 父主表 模式时,管理模型的子表依赖核心参数 module 来绑定子模型的 identifier 标识符,此选项用于开启当前列表是否支持 module 参数,若不打开那么所有管理模式只会针对父模型进行管理,不会干涉到子模型的管理流程。

ajax.position

由于 ExListComplex 组件中已经存在视图管理,即 视图 相关的 view 参数本身就已经和个人设置执行了绑定,此参数用于鉴别 页面位置,示例中的定义可以看到当前页面配置的位置是根据路由中的 type 参数指定。当前选项是一个 Array 类型,所以您可以设置多个值来鉴别位置信息。最简单的场景是:

  • 待审批的工单,position 可以根据工单状态 status 来决定。

  • 已审批的工单,position 可以根据工单状态 status 来决定。

这种场景下您就可以设置 ajax.position 来对位置参数进行设置。

选项:动态

列表中的动态设置主要负责对 操作 进行控制,参考配置如下:

{
    "dynamic.op": false,
    "dynamic.column": false,
    "dynamic.switch": false
}
dynamic.op

此属性用于控制所有 操作类 的选项:

  • false操作类 的所有选项直接以 options 中配置的为准,严格控制了当前列表所有允许的操作配置。

  • true操作类 的所有选项直接通过接口 /api/ui/ops 读取,形成动态配置且可以直接在接口层执行 权限控制

dynamic.column

此属性用于控制 table 中的列配置选项:

  • false:列表的列以 table.columns 中定义的列为准,纯前端资源文件定义列表列。

  • true:列表的列通过接口 /api/columns/<module>/full 读取,此处 module 为当前列表绑定的模型在 zero-crud 中形成的标准化接口。

dynamic.switch

此属性用于控制页面流程相关操作,mode=ADDmode=EDIT 是否执行智能切换。

  • false:不执行智能切换,当您添加一条数据之后,直接返回列表首页(适合基础数据管理场景)。

  • true:执行智能切换,当您添加一条数据之后,直接从添加表单页跳转到编辑页(适合补充更新的场景)。

选项:操作类

操作类的选项从两个维度对当前列表的 操作按钮 进行控制,参考配置如下:

{
    "op.open.add": "添加",
    "op.batch.edit": "批量更新",
    "op.batch.delete": "批量删除",
    "op.extra.column": "修改显示列",
    "op.extra.export": "导出",
    "op.extra.import": "导入",
    "op.extra.view": "视图管理",
    "op.row.edit": true,
    "op.row.delete": true,
    "op.submit.add": "添加",
    "op.submit.save": "保存",
    "op.submit.delete": "删除",
    "op.submit.reset": "重置",

    "id.submit.add": "$opAdd",
    "id.submit.save": "$opSave",
    "id.submit.reset": "$opReset",
    "id.submit.delete": "$opDelete"
}

操作类选项都是设置的列表中按钮上的文字,若不存在则直接删除此操作,存在则表示启用。比较特殊的情况就是设置成布尔中的 false,也会直接被关闭,上述配置中我刻意将 连接点 和操作控制配置分开。

op.open.add

列表内 open 区域( 添加操作区域 )内的主按钮,直接从列表页引导打开 mode=ADD 页签将用户引导到 添加表单页

早期的版本中,open 区域还存在一个按钮 op.open.filter,此按钮负责清除查询条件,这个按钮存在的原因是早期的 ExListComplex 的数据流规划有问题,这个 BUG 已经在新版本中解决了,若您的配置中还碰到了此选项可以直接拿掉。

op.batch.edit

列表内 batch 区域内的 批量更新 按钮,此按钮的触发会受到列表中选择数据的影响,只有您选择了数据记录之后才可点击此按钮。

0

此操作依赖 component 中的 batch.editor 配置定义,由于 批量编辑 窗口的内容和当前模型的 属性 有关联,所以无法设置默认配置,所以若您在列表开发中要打开 批量功能,就必须根据当前的模型提供批量编辑器的配置。

op.batch.delete

列表内 batch 区域内的 批量删除 按钮,此按钮的触发必须让用户选中数据记录才可用(所有的删除级操作都会被 确认类 的选项影响,做前端的一个简单的删除拦截)。

op.extra.column

列表内 extra 区域内的 列筛选 按钮,列筛选按钮可以帮助用户更改当前列的 个人视图,您可以根据您想要查看的模型的列定义个人视图,定义之后您个人看到的列信息会保存在 个人视图 中,并且可通过 视图管理 进行再次调整。

(有默认值)此操作依赖 component 中的 extra.column 配置定义,专用于定制列筛选组件。

op.extra.export

列表内 extra 区域内的 导出 按钮,导出按钮会打开弹窗让用户自定义将要导出的列信息,然后导出 模板化 的文件,现阶段导出版本支持:列选择、列排序 的功能。

(有默认值)此操作依赖 component 中的 extra.export 配置定义,用于定制导出窗口组件。

op.extra.import

列表内 extra 区域内的 导入 按钮,导入按钮会打开弹框让用户上传 模板化 数据文件,然后导入数据记录。

(有默认值)此操作依赖 component 中的 extra.import 配置定义,用于定制导入窗口组件。

op.extra.view

列表内 extra 区域内的 视图管理 按钮,此按钮是一个入口,用户点击之后可进入视图管理界面管理当前环境中的所有 个人视图,个人视图可实现针对视图的 增、删、改 的基本操作。

(有默认值)此操作依赖 component 中的 extra.view 配置定义,用于定制视图专用组件。

上述所有配置中标记了(有默认值)的配置证明此配置不依赖组件配置也会有相关的默认行为,若您想要定制窗口中的各种文字,则可以追加新配置,新配置会覆盖 默认值

op.row.edit

列表行上的 编辑 按钮,若此处为 false 则直接导致编辑功能失效。

op.row.delete

列表行上的 删除 按钮,若此处为 false 则直接导致删除功能失效。

op.row.view

若您关闭了 编辑 按钮,您可以设置此值为查看按钮的文字,直接将行中的 编辑 转换成 查看,这个配置可以让您的系统还原到传统的四页面(列表、添加、编辑、查看)流模式。

关于 行操作 需要说明的是,它的权限会受到几个不同维度的影响,而不单纯的选项关闭,若您的选项打开也有可能导致行操作失效:

  • 选项中优先级最高,要完成行操作的其他定制,此处的行操作相关功能必须处于打开状态。

  • 针对数据记录中会存在 metadata 的属性,其中可控制记录的属性如:

    {
        "deletion": false,
        "edition": true
    }

    标准化行为中,若您的数据字段的 metadata 包含了上述两个属性,它可以控制单行记录的 编辑添加 权限,此处是从系统层去控制。

  • ExListComplex 中可提供插件 $plugins 中的行编辑函数 pluginRow 对行的权限加以控制,计算最终的行权限信息,这个函数可根据每一行的数据独立计算形成按行提供权限。

  • 远程的 zero-rbac 模块配合 zero-ui 也可以对 静态动态 两种不同的访问模式进行 行权限 控制。

上述维度都是更改 行操作 的维度点,不同场景会有所区别。

op.submit.add

当您打开添加页签( mode=ADD ),右上角显示的 添加 按钮的文字设置。

op.submit.reset

当您打开添加页签( mode=ADD ),右上角显示的 重置 按钮的文字设置。

op.submit.save

当您打开编辑页签( mode=EDIT ),右上角显示的 保存 按钮的文字设置。

op.submit.delete

当您打开编辑页签( mode=EDIT ),右上角显示的 删除 按钮的文字设置。

id.submit.add

表单上的 连接点,添加表单( mode=ADD )中的添加按钮。列表页中右上角的按钮位于 <Tabs/> 组件的 Extra 区域,并未位于页签内的表单组件中。

参考如下截图:

0

前文提到的 op.submit.xx 选项设置的是箭头发起位置的按钮文字,箭头指向的地方有表单内配置好的 aiAction 渲染( hidden=true )的表单标准提交按钮,这些提交按钮的 idid.submit 进行设置。参考添加表单中按钮配置:

[
    {
        "metadata": "$button",
        "hidden": true,
        "optionJsx.extension": [
            "$opAdd,添加,SUBMIT,primary",
            "$opReset,重置,RESET"
        ],
        "span": 24
    }
]

所以示例中设置的 id.submit.add 实际就是表单中 $opAdd,添加,SUBMIT,primary 解析的第一个值,而此按钮的触发依靠的是 op.submit.add 这个按钮触发。

id.submit.reset

表单上的 连接点,添加表单( mode=ADD )中的重置按钮。

id.submit.save

表单上的 连接点,编辑表单( mode=EDIT )中的保存按钮。

id.submit.delete

表单上的 连接点,编辑表单( mode=EDIT )中的删除按钮。

选项:窗口类

整个 ExListComplex 有五个子窗口,每个窗口会包含两部分配置

  • 外层窗口配置(窗口类选项配置)

  • 内层组件配置(在 component 中配置)

参考配置:

{
    "window.batch.editor": "选择批量更新的字段,更新,关闭,false,640,true,btnBatchEdit",
    "window.extra.column": "请选择您要显示的列,leftTop,640,true",
    "window.extra.export": "选择导出字段,导出,关闭,false,720,true,btnExport",
    "window.extra.import": "请上传导入文件,导入,关闭,false,720,true,btnImport",
    "window.extra.view": "视图管理,right,400,true,btnView"
}
window.batch.editor

批量编辑弹出窗口配置,组件配置位于 component 中的 batch.editor 中。

window.extra.column

列选择浮游窗口配置,组件配置位于 component 中的 extra.column 中。

window.extra.export

导出弹出窗口配置,组件配置位于 component 中的 extra.export 中。

window.extra.import

导入弹出窗口配置,组件配置位于 component 中的 extra.import 中。

window.extra.view

视图管理抽屉窗口配置,组件配置位于 component 中的 extra.view 中。

选项:Ajax类

远程通信类的配置直接和 zero-crud 中的 15 个API地址绑定,由于操作是位于 Op.js 中,所以单记录的 添加、保存、删除 的 API 配置未存在于选项配置中。

参考如下配置:

{
    "ajax.search.uri": "/api/room-rent/search",
    "ajax.get.uri": "/api/room-rent/:key",
    "ajax.delete.uri": "/api/room-rent/:key",
    "ajax.batch.delete.uri": "/api/batch/room-rent/delete",
    "ajax.batch.update.uri": "/api/batch/room-rent/update",
    "ajax.column.full": "/api/columns/room-rent/full",
    "ajax.column.my": "/api/columns/room-rent/my",
    "ajax.column.save": "/api/columns/room-rent/my",
    "ajax.file.export": "/api/room-rent/export",
    "ajax.file.import": "/api/room-rent/import"
}
ajax.search.uri

HTTP方法:POST,列表页主接口,支持服务端分页、排序、列过滤功能,Zero 中标准的 Qr 语法接口,有时也作为辅助数据提取的接口来使用。

ajax.get.uri

HTTP方法:GET,单记录读取主接口,查看页和编辑页打开之前访问此接口读取数据记录。

ajax.delete.uri

HTTP方法:DELETE,单记录删除主接口,行删除 和编辑页的 删除 按钮都会使用此接口。

ajax.batch.delete.uri

HTTP方法:DELETE,多记录批量删除主接口。

ajax.batch.update.uri

HTTP方法:PUT,多记录批量更新主接口。

ajax.column.full

HTTP方法:GET,若开启了 动态列 功能,列表会从此接口直接拉取模型所有的 可用属性集,而不再使用 table.columns 中的配置,此接口用于加载 全列 配置数据。

ajax.column.my

HTTP方法:GET,不论是否开启 动态列 功能,此接口和后端 zero-rbac 配合读取个人视图,个人视图有两部分内容是会影响列表的:

  • criteria:当前视图使用的默认的查询条件。

  • projection:当前视图使用的默认的列过滤(我的列)信息。

ajax.column.save

HTTP方法:PUT,若您使用了 查询条件 管理和 列筛选保存 两个按钮提供的管理功能时,会触发此接口刷新个人在当前列表的视图信息。

ajax.file.export

HTTP方法:POST,导出方法专用接口。

ajax.file.import

HTTP方法:POST,导入方法专用接口。

上述标准化的接口是 zero-crud 提供,为了简化配置其 HTTP 方法是固定的,这一点可能不太适合您做个人自定义,但由于功能比较完善,直接使用 Zero Extension 中的接口即可,实在要定义可使用 @Adjust 注解直接在后端开发覆盖原始接口。

选项:拦截类

拦截类选项主要用于在操作之前的进一步提示,通常会给用户一个 confirmation 的确认过程。

配置参考:

{
    "confirm.delete": "确认删除当前租借记录?",
    "confirm.batch.delete": "您确认要删除所有选中的租借记录?",
    "confirm.clean.filter": "该操作将清空所有的查询条件,确认?",
    "message.batch.delete": "您所选择的记录已经全部删除成功!"
}
confirm.delete

单记录删除之前的确认,配置之后在删除之前会有提示信息。

confirm.batch.delete

批量删除之前的用户确认信息。

confirm.clean.filter

查询条件删除之前的用户确认信息。

message.batch.delete

批量删除之后呈现的成功信息。

message.batch.update

批量更新之后呈现的成功信息。

列渲染

列表类中的配置节点 table 基本是和 AndD 中的 <Table/> 对齐的配置,所以 列渲染 中没有那么多需要讲解的;本章主要讲解 table.columns 中的列渲染,让开发人员对列的 Zero 中列渲染功能有所了解。

列渲染的 解析器 配置如:

索引 属性 含义

0

dataIndex

绑定的记录对应的属性名。

1

title

当前列的列标题。

2

$render

列的渲染类型,该类型在列表章节详细解析。

3

sorter

是否打开列排序,如果打开则列中会启用排序功能。

4

$KV$

专用键值对处理。

在列渲染中,大部分列都支持如下两个特殊属性:

  • $empty:此属性用于描述文本内容为 null, undefined, "" 时应该呈现的文字信息。

  • $expr:此属性让您呈现的文本可以支持字符串的格式化模式,如:":value间" 这种。

  • style:此属性描述了当前列的样式,不经过 CSS 控制。

  • className:此属性描述了当前列的样式,通过 CSS 控制。

  • width:(数值)若您不需要 Zero 自动计算列宽度(有时候会不准),您可以指定当前列的宽度,那么本列就会使用您定义的固定宽度呈现。

ARRAY

此列渲染用于渲染 [] 格式的数据,将内容渲染成列表:

参考配置:

[
    "username,在住宾客,ARRAY",
]

另一种附加配置,在附加配置中您可以设置风格以及CSS相关配置对列表的样式进行调整。

[
    {
        "metadata": "field,title,ARRAY",
        "style": {},
        "className": ""
    }
]

CONNECT (保留)

此列渲染器为保留的行为连接列渲染器,您可以配置多个链接操作,操作本身可以从 reference 的状态变量中提取。

参考配置:

[
    {
        "metadata": "field,title,CONNECT",
        "$option": [
            "添加",
            "删除"
        ]
    }
]
使用场景

当有多个链接时,您可以构造类似 添加 | 删除 的链接列表,这种模式下 reference 的状态数据结构必须遵循如下

{
    "$connect": [
        {
            "config": {
                "pos": "链接到 column.dataIndex"
            }
        }
    ]
}

CURRENCY

货币格式的列渲染器。

参考配置:

[
    {
        "metadata": "field,title,CURRENCY",
        "$config": {
            "unit": "¥",
            "after": "true | false"
        }
    }
]

货币转换时会自动转换成带有 千分位 的货币数值格式。

配置项 含义

$config.unit

渲染的货币单位。

$config.after

货币单位的位置:

  • true1,200¥

  • false¥1,200

DATE

日期格式的列渲染器。

参考配置:

[
    {
        "metadata": "field,title,DATE",
        "$format": "YYYY-MM-DD",
        "$config": {
            "format": ""
        }
    }
]

此处会根据 format 中定义的模式格式化当前日期信息,只是此处有两种配置:

  • 旧版:(保留)直接使用 $format 配置项来描述日期时间格式。

  • 新版:使用 $config.format 配置来描述日期时间格式。

DATUM

字典(辅助数据)的列渲染器

参考配置:

[
    {
        "metadata": "field,title,DATUM",
        "$datum": "source=bill.type,value=code,display=name",
        "$config": {
            "adorn": {
                "field": "f1",
                "items": {
                    "record[f1]": "icon,size,color",
                    "record[f2]": "icon,size,color"
                }
            }
        }
    }
]

此列渲染器主要负责渲染字典列,它支持的功能如下:

  • 直接绑定当前页面的字典信息:

    配置项 含义

    source

    字典名字,在辅助数据章节有所说明。

    value

    字典值的字段名(后台存储的就是这部分的值)。

    display

    字典显示字段名,这个属性可以支持 $expr 的格式。

    表单中的配置和列渲染中的配置有区别,主要是最后部分,表单是 label=name,而列渲染是 display=name,这是历史原因,这里就不多提了,目前新版的前端 API 针对这种表达式都可执行解析了,但标准配置还是以文档为主,不会出错。

  • 给字典信息追加图标定义,这个功能是修饰功能,此处的 f1 一定会是一个维度字段( 非 主键 类型),所有数据记录中包含几种值,不同的值图标定义可以不相同。

DICT

纯字典类型

参考配置:

[
    {
        "metadata": "field,title,DICT",
        "$config": {
            "field": "xxx"
        }
    }
]

这种字典类型会直接从组件的 props 中提取 特殊字典 $dict 变量中的数据对列进行渲染,此处的 field 默认会使用 name,也可以提取其他属性信息。参考示例如下:

[
    {
        "metadata": "valueNew,新值,DICT",
        "$config": {
            "field": "fieldName"
        },
        "className": "value-column"
    }
]

DOWNLOAD

下载链接渲染列

参考配置:

[
    {
        "metadata": "field,title,DOWNLOAD",
        "$config": {
            "ajax": {
                "uri": "/api/xxxx/download/:key"
            }
        }
    }
]

上述配置会渲染一个下载链接,下载链接中包含了 ajax 的部分(可自定义),此处存放的就是下载链接地址,点击之后就可以下载相关文件。

EDITOR

自定义表格列编辑器

参考配置:

[
    {
        "metadata": "field,title,EDITOR",
        "$config": {
            "render": "aiSelect",
            "optionJsx.config.items": [
                "GET,GET方法",
                "PUT,PUT方法"
            ]
        }
    }
]

这种渲染一般用于自定义表格列编辑器,它会渲染行内编辑行为,目前支持的行内编辑组件如下(由于没有全部做过测试,此处只是根据源代码中梳理的组件逻辑,验证过的是目前已经在使用的):

支持的渲染器
渲染器 验证 含义

aiInput

Ok

(默认值)文本框

aiInputNumber

Ok

数值输入器

aiRadio

Ok

单选框

aiTreeSelect

Ok

树型下拉框

aiCheckbox

Ok

多选框

aiTimePicker

Ok

时间选择器

aiDatePicker

Ok

日期选择器

aiSelect

Ok

下拉框

aiProtocol

Ok

协议输入器

aiCaptcha

Ok

验证码输入

aiPassword

Ok

密码输入

aiBraftEditor

富文本输入框

aiAddressSelector

地址选择器

aiCheckJson

JSON 格式多选器

aiTransfer

穿梭框

aiFileBatch

批量上传

aiFileLogo

图片上传

aiFileUpload

上传组件

aiInputArray

数组型文本录入

aiJsonEditor

JSON 编辑器

注:此处作为表格内编辑器时,配置项直接位于 $config 内,除了 render 以外,其他配置和表格中配置等价。

EXECUTOR

操作列专用渲染器

参考配置:

[
    {
        "metadata": "field,title,EXECUTOR",
        "$option": [
            {
                "text": "编辑",
                "executor": "fnEdit"
            },
            "divider",
            {
                "text": "删除",
                "executor": "fnDelete",
                "confirm": "确认删除选择记录"
            }
        ]
    }
]

上述配置是常用列表的配置,一般配置 EXECUTOR 时必须通过编程的方式从外层填充 $executor 变量。

ExListComplex 组件中的 $executor 变量一般专用于行操作,它可以直接配合 EXECUTOR 来完成行操作的 定制 流程,此变量是一个 Object,它的键和 $option 选项中的 executor 直接对应,内置了 fnEdit, fnDelete(标准列表中的函数)。

参考下边的返回数据结构:

    executeFn: (reference) => ({
        rxEdit: (key) => {
            Ux.ajaxGet("/api/night/:key", {key}).then(response => {
                const state = {};
                state.$keyWindow = "view";
                state.$visible = true;
                state.$inited = response;
                reference.setState(state);
            })
        }
    })

注意上述结构中实际是使用了 二阶函数 在生成 $executor 对象,上述代码段可配置的 executor 为 rxEdit

  • 默认内置的函数一般使用 fn 前缀做函数名,表示 Internal Function

  • 若是用户自己定义的函数一般使用 rx 前缀做函数名,表示 Reactive Execution

此处的 executor 函数定义如下:

参数表

(key, record, configuration)
参数含义
参数名 子项 含义

key

当前行记录的键信息,此处 key 的值直接对应 "field,title,EXECUTOR" 中的 field,即配置的 dataIndex 的值。

record

当前行的完整记录信息,此处的行记录为全行记录信息,它包括额外的没有在 table 中定义 columns 的属性信息。

configuration

config

直接对应当前列的完整配置信息( Column )定义中的信息

configuration

ajax

当前 Option 独立的远程通信信息,定义在独立的 Option 内部

configuration

parameters

当前 Option 对应的参数信息,此参数信息目前主要用于处理 message 消息部分。

configuration

reference

引用的 React 组件相关信息。

$executor 的扩展配置实际是整个开发流程中 高频 定制的地方,它让您的 ExListComplex 多了很多 行扩展 的可能性,您可以直接根据您的需要在一个列表的行中定制各种所需的的操作,唯一不能定制的是此处的操作是链接,此处无法定制其他样式的操作按钮。

FILE_SIZE

文件尺寸专用渲染器

参考配置:

[
    {
        "metadata": "field,title,FILE_SIZE"
    }
]

此列渲染器专用于渲染文件尺寸,它把一个数值直接转换成文件尺寸,并且自动追加单位:B, KB, MB, GB, TB

链接渲染器

参考配置:

[
    {
        "metadata": "name,名称,HYPERLINK",
        "$config": {
            "url": "/person/circle-view?id=:key"
        }
    }
]

其配置项如:

配置项 含义

$config.url

当前链接制定的路由地址

  • 此地址不会使用 <a href/> 的方式驱动,而是直接调用 react-router 的方式驱动。

  • 此地址可以支持全行数据的表达式做参数,如上述地址中定义的 :key 的模式化参数会替换成 key 属性的值,参考 Ux.formatExpr 函数。

  • 此地址中不用配置 Z_ROUTE 前端部分的前缀,仅配置实际路由地址即可,应用层的 context 部分会自动追加。

LAZY

延迟加载列渲染器

参考配置:

[
    {
        "metadata": "openBy,制单人,USER",
        "$config": {
            "uri": "/api/user/:key",
            "field": "realname",
            "icon": "user,#00BF9F"
        }
    }
]

其配置项如:

配置项 含义

$config.uri

此处一般是 GET 方法,此属性定义了延迟关联数据的访问接口。

$config.field

远程读取的数据一般是 {} 的结构,而 field 定义了呈现在界面上的字段名,如示例中呈现的是读取到的用户数据的 realname 属性。

$config.icon

是否在呈现的属性中追加图标信息,type,color 的格式。

此处有一点需说明,这个 列渲染器 有两个值:

  • USER:旧值,由于最早这个列渲染器只是单纯渲染类似创建人、签单人、更新人的信息,所以使用了该值。

  • LAZY:新值(推荐之后的系统使用),渲染内容的业务逻辑取决于您定义的 $config.uri 接口中提取的数据。

定义的接口的语义:根据当前属性的值填充 :key 参数读取单条数据。

而且有一点,Ux 中有一个API ajaxEager,此 API 存在的目的就是为这个功能量身打造的,它可以让系统在处理此部分内容时实现归并和压缩——如两条数据的 createdBy 都是同一个人时,整个系统只会让你发送针对这个人的数据请求,而不是发两次请求,这种设置大大减少了当前列表中辅助数据的远程交互频次。尽管有这样一个机制,依旧不推荐在多个列中同时使用延迟列,毕竟这种设置只能从 行维度 去减少交互次数,不会从 列维度 去减少交互次数。

LOGICAL

布尔值渲染器

参考配置:

[
    {
        "metadata": "income,消费/付款,LOGICAL",
        "$mapping": {
            "true": "消费项,pay-circle,16,#268941",
            "false": "付款项,pay-circle,16,#f6af03"
        }
    }
]

注意此处启用了特殊属性 $mapping 而不是 $config,这也是为了契合映射语义设计的机制,它表示从 呈现 的强语义关系。此处的值的配置启用了 解析表达式,对应的语义为:

索引值 属性名 含义

0

text

值对应的显示文本

1

icon

此值定义的图标信息

2

size

定义图标时图标的大小(推荐使用 `14 / 16`等)

3

color

定义了图标的色彩

MAPPING

多值映射渲染器

参考配置:

[
    {
        "metadata": "method,HTTP方法,MAPPING",
        "$mapping": {
            "GET": "GET,download,,#268941",
            "PUT": "PUT,edit,,#0a7bed",
            "POST": "POST,plus,,#f6af03",
            "DELETE": "DELETE,delete,,#e22015"
        }
    }
]

此渲染器和 LOGICAL 使用的配置用法完全一致,只是二者的使用场景不同:

  • LOGICAL 一般用于后端的属性是 boolean 值的情况,字面量只有 true / false,它属于 MAPPING子集

  • MAPPING 不限定类型,适用性更广泛,但弱化了布尔值(开关型)的语义,所以才有会单独的 LOGICAL 的渲染器。

PERCENT

百分比渲染器

参考配置:

[
    {
        "metadata": "field,title,PERCENT"
    },
    ...,
    "field,title,PERCENT"
]

此渲染器是百分比渲染器,目前不支持任何配置,只是单纯把浮点数如 0.123 转换成 12.3%,后期可以考虑追加精度来完成百分比的增强渲染(注意此时值必须是一个可以转换成数值的值,不可以是 NaN)。

PURE

纯文本高亮显示器

参考配置:

[
    {
        "metadata": "field,title,PURE",
        "highlight": true
    },
    ...,
    "field,title,PURE"
]

支持 高亮 语法的渲染器,通常会根据您输入的关键字对文本中内容执行高亮,当系统启用高亮语法时,您需要保证您的 React 组件状态 state 拥有如下数据结构:

{
    "$keyword": {
        "field1": "xxxx",
        "field2": "xxxx"
    }
}

此处的 field1, field2 若和当前 dataIndex 匹配的话,那么对应的 xxxx 在文本值中的部分会直接被高亮呈现。

RENDERS

编程模式,自定义扩展型渲染器

参考配置:

[
    {
        "metadata": "sourceCode,配置项编号,RENDERS",
        "config": {
            "value": "code",
            "mapping": {
                "globalId": "sourceGlobalId",
                "name": "sourceName",
                "identifier": "sourceIdentifier"
            }
        }
    }
]

这是一个特殊渲染器,它提取的是 React 组件属性 props 中的 $renders 变量,此变量结构如下:

{
    "field1": (props) => {

    },
    "field2": Jsx
}

您可以从外层直接传入两种不同形态的渲染模型(函数型、组件型),系统会自动按照 Zero 中的继承模式将对应属性传入到渲染器中,这个组件接收到的核心 props 中的变量如:

{
    "value": "当前属性的值",
    "config": "当前列的配置",
    "data": "当前行记录全值"
}

ROW

早期以为 EDITORROW 的性质相同,现在发现此两种列渲染区别很大:

  • EDITOR:多用于自定义表格中处理,且 <Form/> 可以将整个表格的列单独抽取成可编辑的。

  • ROW:通常用于行编辑(带索引)

    • 若在 <Form/> 中,则 reference.props.value 的值可以作为表单中渲染的值。

    • 若在外层中,也可以使用 reference.props.value 来处理控制列等信息。

参考配置:

[
    {
        "metadata": "field,title,ROW",
        "$config": {
            "field": "aiTreeSelect",
            "jsx": {
                "style": {

                },
                "config": {
                    "datum": "source=term.expense,key=key,label=code",
                    "__COMMENT": "xxxx"
                }
            }
        }
    }
]

常用的配置数据 $config 如下:

正常模式下,ROWEDITOR 应该具备近似度很高的配置结构,但二者使用场景上的区别,此处最初设计上有很大的失误,现阶段已经无法更改此处的配置结构,暂时保留此处配置的数据结构,加上从 AntD 3.x → 4.x → 5.x 过程中 onChange 函数也发生过不小的该变,所以此处就不再调整了,保留两种使用,开发人员了解使用场景即可。

属性 含义

field

等价于 EDITOR 中的 render 属性,用于选择表单选择器

jsx

等价于 EDITOR 中的 optionJsx 属性,用于提供当前表单操作组件的配置信息,在表单中等价于 optionJsx 属性。

fieldCond

默认值( key ),条件属性,在执行索引查找时专用的条件属性

fieldKey

已废弃),新版本会直接连接 format.keyField 做统一,以防止主键不统一的情况。

代码中的属性使用

// value = []
// fieldCond = `key`
// record.key = record[format.keyField]
const foundIndex = __Zn.elementIndex(value, fieldCond, record[format.keyField]);

TAG

标签渲染器,标签渲染器相对比较简单,根据您的数据值的不同格式执行从 <img/><Icon/> 切换:

格式 含义

text

直接将值转换成 <Icon/> 中的 type 属性进行渲染。

tag:text

text 转换成 <img/> 标签中加载的系统默认的图片 src 来对待。

新版标签渲染器追加了标签的独立渲染功能,可直接渲染 antd 中的 <Tag/> 标签,生成如下列信息:

exp list col tag

新版标签渲染器需配合 zero-ambient 扩展模块一同使用,用于在某些记录中直接打标签,实现记录的标签化处理,且对应到后端的 X_TAG 表结构中,其中此处列渲染要启用基础配置信息,配置片段示例如下:

    {
        "metadata": "color,标签颜色,TAG",
        "$config": {
            "items": [
                "geekblue,极客蓝",
                "magenta,粉红",
                "red,红色",
                "volcano,火山红",
                "orange,橙色",
                "gold,金色",
                "lime,青柠绿",
                "green,绿色",
                "cyan,青色",
                "blue,蓝色",
                "purple,紫色"
            ]
        }
    }

还有一点需注意是这种渲染器是为 antd 中的 <Tag/> 标签量身打造,所以此处的颜色 只能移除部分而不能追加新的 ,即此处的颜色信息是一个固定列表。

TEXT

(默认)文本渲染器,此渲染器是唯一一个不需要配置的渲染器,属于 默认渲染器,此渲染器到处都是,就不详细拆解了。

TOTAL

求和渲染器(计算渲染器)

参考配置:

[
    {
        "metadata": "amount,金额,TOTAL",
        "$config": {
            "currency": "¥",
            "op": "M",
            "field": [
                "quantity",
                "price"
            ]
        }
    }
]

其配置项如:

配置项 含义

$config.currency

货币单位

$config.op

计算符号

  • P:加法

  • M:乘法

$config.field

字段合计,针对哪些字段执行求和操作(一般是同行的属性)

列过滤

前边章节讲解了 列渲染 相关内容,最后看看列表中的 列过滤,列过滤部分直接上配置,主要配置如下:

  • $filter.type:列过滤类型。

  • $filter.config:此类型对应的配置信息。

搜索框类型:

[
    {
        "metadata": "name,圈子名称,,true",
        "$filter.type": "SEARCH",
        "$filter.config": {
            "placeholder": "输入名称",
            "button": {
                "search": "搜索",
                "reset": "重置"
            }
        }
    }
]

DIRECT (默认)

布尔选择类型:

[
    {
        "metadata": "active,是否启用,LOGICAL,true",
        "$mapping": {
            "true": "启用",
            "false": "禁用"
        },
        "$filter.config.dataType": "BOOLEAN",
        "$filter.config.items": [
            "true,启用",
            "false,禁用"
        ],
        "$filter.config.button": {
            "yes": "确认",
            "reset": "重置"
        },
        "$filter.config.width": {
            "radio": 110,
            "button": 55
        }
    }
]

DATUM

字典类型:

[
    {
        "metadata": "floor,楼层,DATUM",
        "$datum": "xxxxx",
        "$filter.type": "DATUM",
        "$filter.config.button": {
            "yes": "确认",
            "reset": "重置"
        }
    }
]

定制化开发

本章讲解列表定制化开发部分,当您使用了列表组件 ExListComplex / ExListFast 之后,您可以参考本章的引导对标准列表进行操作定制,列渲染和过滤本身的定制目前先不讲解,后续遇到之后会逐步开放。

本章节的定制化开发主要是针对开发人员对标准列表进行扩展和定制开发,所以不支持直接配置的模式,后期版本开通 SSR 功能之后可以让行为在服务端实现直接定制,就可以打开配置功能。

添加区域

定制步骤

在标准的列表中( ExListComplex 组件),它的头部主要分为了四个区域,本章节定制添加区域,参考如下截图:

exp list define open

本章节详细讲解如何拓展新增区域并追加一个按钮以实现次区域的定制,您可以按照如下步骤进行:

  1. options 节点中配置 op.extension.xxx 这种前缀为 op.extension 的操作按钮节点

    {
        "_grid": {
            "...": "...",
            "options": {
                "...": "...",
                "op.extension.generate": {
                    "text": "批量发卡",
                    "icon": "build",
                    "className": "uc_green",
                    "region": "op.open.card",
                    "config": {
                        "executor": "rxCard"
                    }
                },
                "op.extension.reject": {
                    "text": "批量拒绝",
                    "region": "op.batch.reject",
                    "plugin": {
                        "prompt": "confirm.batch.reject"
                    },
                    "config": {
                        "index": 1,
                        "executor": "rxReject",
                        "ajax": {
                            "uri": "/api/todo/reject",
                            "method": "POST"
                        },
                        "message": "message.batch.approval"
                    }
                }
            }
        }
    }

    在 Zero UI 中有一个基本限制,就是扩展 ExListXxx 类似的组件时,所有扩展区域的按钮必须以 op.extension 作为操作前缀,只有这种类型的前缀会被捕捉到并扩展到相对应的区域。解读一下上述配置的具体细节:

    配置项 含义

    op.extension.generate

    这是最早的配置项,不同的按钮其名称有所区别,这个名字只要不重复,您就可以无限扩展(当然也取决于您的添加区域的宽度),此处的按钮系统名称为 op.extension.generate

    text

    此按钮显示的文字信息。

    icon

    此按钮上显示的图标,图标扩展会追溯到 AntD 3.x 中的 <Icon type=""/>,现在虽然升级了不能使用这种语法,可 Zero Ui 做了版本的兼容处理,这种配置依旧生效( Ux.v4Icon 方法)。

    className

    当前按钮的风格信息,常用的风格参考对应章节,uc_ 开头,全称为 Uniform Color

    region

    这个属性比较重要,按照列表本身的区域配置,头部会包含四个核心区域:open, batch, search, extra,这四个区域中支持按钮扩展的只有 open, batch, extra,所以此处命名方式遵循约定赋值到 region 属性中,此属性的含义如下:

    • op.open 前缀:表示按钮会追加到 open 区域(本例中)。

    • op.batch 前缀:表示按钮追加到 batch 区域。

    • op.extra 前缀:表示按钮追加到 extra 区域。

    如示例中实际拓展了两个按钮,一个位于 open 区域,一个位于 batch 区域。

    plugin

    插件体系一般是按钮在 batch 区域时使用,此插件体系的完整版如:

    {
        "op.extension.approval": {
            "...": "...",
            "plugin": {
                "window": "window.batch.approval",
                "componentType": "ExApprovalBatch",
                "component": "batch.approval"
            }
        }
    }

    此处的节点配置主要做对接扩展,配置的内容是自由格式,您可以根据自己按钮部分的定制选择使用哪种配置,此处的 approval 插件是参考的批量处理按钮的配置在设置,但实际格式取决于您开发的扩展按钮的内部逻辑。

    config

    配置项,配置项是半自由格式,主要两个影响前端布局的属性如下:

    • executor:当前执行函数的函数名,一般是点击操作之后的函数绑定专用。

    • index:当前按钮的顺序,有了 index 之后可以对您的 extension 扩展进行排序,所有的 extension 的按钮基本位置是追加在原始的 op.batch 按钮之后,可以使用 index 进行重排。

  2. rxReject 处配置的 executor 定制外置函数。

    const rxReject = () => (reference, config = {}) => (event) => {
        Ux.prevent(event);
        return Ux.sexBatch(reference, (keys = []) => {
            const {ajax = {}} = config;
            return Ux.asyncPromise(ajax, {keys});
        }, {name: "rxBatchApproval", message: config.message});
    };

    此处注意,上述函数此处是一个三阶函数,每一阶含义如下:

    1. 三阶:(),此处虽然没有带参数,但实际使用过程中它在最外层中,您实际是可以传入您想要的外置参数的。

    2. 二阶:(reference, config = {}),此处两个参数有待注意,reference 实际是组件 ExListXxx 的引用,而不是外层引用;config 则包含了上述配置中的 config 节点之下的所有内容。

    3. 一阶:(event),直接绑定到按钮点击事件中。

  3. 再看一个 rxCard 对应的 executor 的配置:

    const rxCard = (ref) => (reference) => (event) =>
        Ux.of(ref).open().done();

    此处的基础定义和前一步骤是一致的,同样是一个三阶函数,每一个阶的含义如下:

    1. 三阶:(ref):此处带有参数,实际 ref 表示的是最外层组件的组件引用,此处属于外置参数

    2. 二阶:(reference):此处没有开放 config 函数,其实此处也不需要开放第一参,只是单纯为了让开发人员找到 ExListXxx 的组件引用,所以保留了 reference 参数。

    3. 一阶:(event),直接绑定到按钮点击事件中。

高阶 vs 绑定

注意:在列表定制开发中,此处头部区域的按钮绑定的都是 三阶函数,此处实质上 ExListXxx 组件中消费都是二阶函数,参考如下图示:

exp list action 3

实际上这个三阶函数的第三阶已经在调用组件外层就转换成二阶了,传入组件之后还会执行一次组件引用的绑定 (reference, config),这次绑定会彻底生成最下层的按钮所需的 (event) 事件函数,这样的结构就让开发人员在书写函数时可以根据自己需要选择对应的引用去提取相关数据。这种设计在 Zero UI 中十分常见,很多开发人员不理解高阶函数的实用场景,参考下图:

exp list split

  1. 如果您的事件函数体书写在 UI.js 中,结构/行为/显示三者不分离,此时函数内可以使用 this 拿到 React 组件引用。

  2. 但是若您将原来的 UI.js 拆分成 UI.js 和 Op.js 两个文件时,想要在 Op.js 中拿到 React 组件引用,您就可以考虑多一阶的方式去处理。

  3. 如此操作之后,您依旧可以在函数内部使用 reference 代替 this 拿到组件引用。

此处诱发另外一个思考:为什么不直接使用 Function.bind 的方式来切换函数本身的 this 作用域呢(最少Zero Ui中不推荐这种方式)?

在 Zero Ui 中大量使用了高阶替换了 this 作用域的绑定,其考虑点如下:

JavaScript 中的函数 this 作用域已经被 ES6 的箭头函数改掉了,它本身并非以 OO 范式设计的语言,this 本身的设计目的是在函数内部,指代函数当前的运行环境,早期的 OO 玩法是使用原型链打造近似于 class 的语法,但 ES6 吸取了大量语言的优点,进行了改动。这种模式下,若再考虑 Function.bind 的方式切换函数本身的作用域,有可能会造成作用域的管理不受控,这是Zero Ui使用高阶代替绑定( bind的主要原因。再者在 React 框架中,很多时候组件本体的继承树会出现多层结构(可能您的组件结构没这么复杂),当这种结构出现时,this 的作用域管理会和组件引用产生一定的绑定关系,但这种绑定会导致开发人员无法清楚判断 this 的作用域引起不必要的 BUG,最典型会让函数本身变得依赖 this 导致副作用感染区域过大——绑定错误直接引起重用性问题,所以基于此考虑,Zero Ui推荐使用高阶的方式,这种方式职责更清晰,就像上述示例中 ref 代表外层组件引用,reference 代表内层组件引用。

当然,不推荐部代表您不可以使用 Function.bind,您还是可以在环境中这样用,只要您书写的函数本身具备可观测性即可。

行区域

在标准的列表中( ExListComplex 组件),行区域定制主要针对表格行操作链接(默认是链接,您可以切换成按钮),参考下图(角色管理):

exp list action r

默认规则

Zero Ui 中的 ExListXxx 类型的列表组件的列渲染中有一类 EXECUTOR 用于处理操作列,先参考角色管理中的列处理配置:

[
    {
        "title": "操作",
        "dataIndex": "key",
        "fixed": "left",
        "$render": "EXECUTOR",
        "$option": [
            {
                "text": "编辑",
                "executor": "fnEdit"
            },
            "divider",
            {
                "text": "删除",
                "executor": "fnDelete",
                "confirm": "确认删除选择的角色记录?"
            },
            "divider",
            {
                "text": "权限设置",
                "executor": "fnAuthorize"
            }
        ]
    }
]

上述配置是角色管理中的三个按钮的基本配置,在列表行区域的默认规则如:

executor 名称 含义

fnEdit

对应编辑表单专用的执行器,表单对应 FormEdit

fnDelete

对应删除操作专用的执行器,表单对应 FormEdit

在通用的中后台管理模块中,fnEditfnDelete 属于默认配置,基本规则如下:

  1. 二者配置的时候无需额外配置,直接配置后会按照列表的基础配置呈现并执行相关规则。

  2. 您可以直接使用 divider 值呈现两个链接之间的分割线,这种格式配置为固定配置。

  3. 若您未配置 fnEdit,则可以默认开启查看功能(只读模式):

    {
        "op.row.edit": false,
        "op.row.delete": false,
        "op.row.view": "查看"
    }
扩展行

Zero Ui中扩展行配置如前边角色扮演所示,直接配置过程中您可以追加一个新的按钮如:

[
    {
        "text": "权限设置",
        "executor": "fnAuthorize"
    }
]

注意:这种模式并非纯配置模式,而是依赖编程开发扩展函数,此函数的脚本示例如下:

    <ExListComplex  {...Ex.yoAmbient(this)}
                    $executor={{
                        fnAuthorize: (roleId, record = {}) => Ex.aclRoute(this, {
                            key: roleId,
                            view: "ROLE",
                            data: record,
                        })
                    }}
                    config={config} $form={form}/>

上述 fnAuthorize 为行定制专用函数,此函数签名您可以参考列渲染章节,此处由于开发脚本存在于同一个文件 UI.js 中,所以并没有使用高阶函数绑定事件,而是直接定义了函数,采用了 this 引用。

关于四个区域的扩展,等后边深度定制的时候再提供教程来梳理,不同区域的函数签名略微有些差异。

示例:extra 操作

本章节看一个详细的例子(零点提供)来查看右上角的定制部分:

0

此处可以看到,点击进入详情页之后,此处的按钮已经被重新定制过了,且定制成了自定义的模式(此处使用了 Smart 开发模式)。

  1. 关闭和定制列表的选项:

    {
        Options: {
            rm: [
                "form.add",         // 关闭添加表单
                "form.filter",      // 关闭高级搜索表单
                "op.open.add",      // 按钮:添加
                "op.extra.export",  // 按钮:导出
                "op.extra.import",  // 按钮:导入
                "op.batch.edit",    // 按钮:批量编辑
                "op.batch.delete",  // 按钮:批量删除
                "op.submit.add",    // 内页:添加提交
                "op.submit.save",   // 内页:编辑提交
                "op.submit.delete", // 内页:删除提交
                "op.submit.reset",  // 内页:重置
            ]
        },
        Form: {
            name: "FormBook",
            FormEdit
        }
    }

    有一点注意,此处的 FormEdit自定义编辑表单

  2. 挂载右上的按钮部分:

        yoRx: (reference) => ({
            rxPostClose: () => Fn.ioOrderClose(reference, {$booked: false}),
            rxExtraEdit: (data, ref, fnJsx) => Jx.aiPayMenu(reference, data, fnJsx),
        })

    此处挂载了两个 rx 函数,其含义分别如下:

    • rxPostClose:此函数和右上的按钮没有任何关系,只是单纯提供了核心函数方法,此方法在关闭页签之后触发:Post Close

    • rxExtraEdit:此函数为核心渲染方法,它有三个参数:data 表示当前界面绑定的远程数据记录的基础数据;ref 是外层的 React 组件引用;fnJsx 是挂载的外层 Jsx 渲染器,界面呈现就是依赖此处的 rxExtraEdit 来扩展定制。

  3. 行操作触发函数(行区域扩展):

        yoExecutor: (reference) => ({
            fnIncome: Ex.rxRowOpen(reference, {
                // 打开后操作
                rxAfter: (bookKey, record) => Fn.ioOrderOpen(reference, record, {bookKey}),
            }),
            fnView: Ex.rxRowOpen(reference, {
                // 调用订单关闭时的操作
                rxAfter: (id, record) => reference.setState({$booked: true}),
            }),
        })

    这里调用了 Ex 中的 rxRowOpen 来打开行相关内容,说明一下几个点:

    1. 此处的 fnIncome / fnView 就是行配置中的 EXECUTOR 配置中对应的 executor 配置属性。

    2. rxAfter / rxBefore 是所有的 executor 自定义扩展都可以直接定义的前置操作后置操作

  4. 行状态控制——行状态控制是更加细粒度的控制操作,您可以根据当前行的记录数据控制哪些链接显示哪些链接不显示,此处主要定制:koRow 函数:

        yoPlugins: (reference) => ({
            // 行操作
            koRow: (record, config, ref) => {
                if ("Finished" === record.status) {
                    return "fnView" === config.executor;
                } else {
                    return "fnIncome" === config.executor;
                }
            }
        })

    先解释下上述的控制逻辑,直接检查记录中的 status 属性:

    1. status=Finished 表示已完成的账单,这种情况会触发 fnView 的执行器( fnView 执行器是直接从 fnEdit 转换过来的查看执行器,不用配置,切换查看页面流专用 )。

    2. status 的值不是 Finished 则执行 fnIncome 的执行器,继续执行管理操作。

    这样配置之后,这个模块的行就包含了行双态,行的数据会影响行的按钮行为,这个功能在很多场景中都可以使用。

此处放出完整的定制模块源代码,让读者对此有更深刻的理解:

UI.json

{
    "_assist": {
        "tabular": {
            "uri": "/api/types/tabulars",
            "method": "POST",
            "magic": {
                "$body": [
                    "preorder.category",
                    "preorder.method",
                    "surety.type",
                    "code.source",
                    "code.market",
                    "code.pricecat",
                    "order.status",
                    "zero.customer",
                    "gender.type",
                    "idc.type",
                    "bill.type",
                    "bill.status",
                    "bill.category",
                    "pay.term.type",
                    "in.room.type"
                ]
            },
            "group": "type"
        },
        "room.type": {
            "uri": "/api/room-type/by/sigma"
        },
        "code.price": {
            "uri": "/api/room-price/hotel/:hid",
            "magic": {
                "hid": "PROP:app.mHotel.key"
            }
        }
    },
    "_grid": {
        "query": {
            "criteria": {
                "sigma": "PROP:app.sigma",
                "": true,
                "major": "BOOL:false"
            }
        },
        "module": {
            "NAME": "账本",
            "MODULE": "fm-book",
            "IDENTIFIER": "fm.bank"
        },
        "options": {
            "search.advanced": false,
            "op.row.edit": false,
            "op.row.delete": false,
            "op.row.view": false,
            "tabs.edit": "账务管理"
        },
        "table": {
            "columns": [
                {
                    "title": "操作",
                    "dataIndex": "key",
                    "fixed": "left",
                    "width": 80,
                    "$render": "EXECUTOR",
                    "$option": [
                        {
                            "text": "账务处理",
                            "executor": "fnIncome",
                            "ajax": {
                                "uri": "/api/order/:orderId"
                            }
                        },
                        {
                            "text": "查看账本",
                            "executor": "fnView"
                        }
                    ]
                }
            ]
        },
        "synonym": {
            "modelKey": "房号"
        }
    }
}

UI.js

import Ui from "ui";
import Ex from 'ex';
import FormEdit from './UI.Form';
import {Fn, Jx} from "app";
import Ux from "ux";

export default Ui.smartList({
    ns: require("./Cab.json"),
    name: "PxBook",
    Options: {
        rm: [
            "form.add",         // 关闭添加表单
            "form.filter",      // 关闭高级搜索表单
            "op.open.add",      // 按钮:添加
            "op.extra.export",  // 按钮:导出
            "op.extra.import",  // 按钮:导入
            "op.batch.edit",    // 按钮:批量编辑
            "op.batch.delete",  // 按钮:批量删除
            "op.submit.add",    // 内页:添加提交
            "op.submit.save",   // 内页:编辑提交
            "op.submit.delete", // 内页:删除提交
            "op.submit.reset",  // 内页:重置
        ]
    },
    Form: {
        name: "FormBook",
        FormEdit
    },
    yoRx: (reference) => ({
        rxPostClose: () => Fn.ioOrderClose(reference, {$booked: false}),
        rxExtraEdit: (data, ref, fnJsx) => Jx.aiPayMenu(reference, data, fnJsx),
    }),
    yoExecutor: (reference) => ({
        fnIncome: Ex.rxRowOpen(reference, {
            // 打开后操作
            rxAfter: (bookKey, record) => Fn.ioOrderOpen(reference, record, {bookKey}),
        }),
        fnView: Ex.rxRowOpen(reference, {
            // 调用订单关闭时的操作
            rxAfter: (id, record) => reference.setState({$booked: true}),
        }),
    }),
    yoPlugins: (reference) => ({
        // 行操作
        koRow: (record, config, ref) => {
            if ("Finished" === record.status) {
                return "fnView" === config.executor;
            } else {
                return "fnIncome" === config.executor;
            }
        }
    }),
    componentInit: (reference) => {
        Ex.yiAssist(reference)
            .then(Ux.ready)
            .then(Ux.pipe(reference));
    },
    componentYo: (reference, inherit = {}) => {
        const {$booked} = reference.state;
        if ($booked) {
            // 查看账本,如果有特殊逻辑则在此处加新代码
        } else {
            // 订单处理
            Fn.yoBill(reference, inherit);
        }
        // 设置 $entry
        inherit.$entry = "BILL";
        return inherit;
    }
})

若您仔细阅读了前边章节的列表定制,那么这样两份代码是没有任何难度的;除了本章提到的扩展以外,实际在定制过程中还有很多扩展方式无法一一梳理,也没法书写相关细节,等到后续案例章节来一一说明。

自定义开发

组件:DocViewer

本章带大家一起开发一个文档阅览器,文档阅览器主要用于从远程 X_ATTACHMENT 生成的属性中加载对应的文件信息,实现 Word, PPT, Excel, PDF 四种不同文件的阅览功能,这是一个新的自定义组件,且不会生成 aiX交互式组件,仅做文档预览专用。开发文档阅览器使用的库如:

旧版本从2020年开始已经没有运维了(最新版 0.1.5 ),暂时可以搁置在那儿,最新版直接参考上述链接地址,最新版为 v1.13.0,其用法一样,后续 Zero Ui会将这部分内容原生支持到框架内部进行持续改造,按开源协议拉一个分支出来自己维护

环境搭建

在您的项目中执行如下命令添加依赖包:

 # NPM 模式安装
 npm install --save @onlyoffice/document-editor-react
 # YARN 模式安装
 yarn add @onlyoffice/document-editor-react

这是一个标准化组件,可以直接扔到 economy 中去实现,实际在这个项目内您可以看到目录前缀( @ 符号之前 )如下截图:

uca component economy

上述结构中 @ 之前的拼写表示这个组件来自于哪个内部项目,即 z 开头的一系列底层项目,由于 DocViewer 的目的是做文档阅览,所以此处选择直接放到最上层处理:

前缀 目录 含义

web

economy

直接在标准组件中定义文档阅览器,此处有个特殊点在于此文档阅览器会解析 X_ATTACHMENT 的数据结构,所以更好的方式是直接放到 extension 部分,但我希望Zero核心框架中自带文档阅览器,和拓扑分析图一样,形成一个最基础的组件来加载不同的二进制文件流。

按照如下步骤创建此自定义组件( 自定义组件只能在项目 https://gitee.com/silentbalanceyh/scaffold-ui.git 中开发,ai sync 命令会读取此项目做框架层同步 ):

  1. economy 目录下创建一个新目录:web@DocViewer

  2. 按照 Zero Ui 的基础规范,目录中创建入口文件 UI.js,文件内容如下:

    旧版写法(class模式)

    import React from 'react';
    
    class Component extends React.PureComponent {
        render() {
            return (
                <div>
                    <h1>Doc Viewer</h1>
                </div>
            );
        }
    }
    
    export default Component;

    新版写法(hook模式)

    import React from 'react';
    
    const Component = (props) => {
        return (
            <div>
                <h1>Doc Viewer</h1>
            </div>
        );
    }
    
    export default Component;

    新旧版本的选择在于您是否要启用 Ux.zero注解,若使用注解和资源文件绑定,那么依旧采用 class 模式开放是最好的选择,若您的组件内部无需启用资源文件绑定的功能,那么可以直接使用 hook 模式开发。

  3. 写好之后更改入口文件 index.js / index.d.ts

        // 都追加如下导出信息:
        export {default as DocViewer} from './web@DocViewer/UI';
    
        // 消费端使用:
        import {DocViewer} from 'web';
  4. 提交 scaffold-ui 中的 PR 并且在自己项目中执行 ai sync 同步对应代码就可以使得此组件生效了。

文件下载

文档阅览器中使用的数据结构在 Zero Ui 中是直接对齐了 X_ATTACHMENT 记录的数据结构,此数据结构如:

{
    "key": "???",
    "name": "论文范文.docx",
    "extension": "docx",
    "type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "size": 16644,
    "status": "DONE",
    "directoryId": "???",
    "storeWay": "STORE",
    "storePath": "/apps/nm/document/合规文档/法规库/LAW2309071001/论文范文.docx",
    "modelId": "nm.law",
    "modelKey": "???",
    "modelCategory": "DOC.NORM.LAW",
    "fileName": "论文范文",
    "fileKey": "???",
    "fileUrl": "/api/file/download/xxx-of-download",
    "filePath": "file-uploads/xxx-of-upload",
    "active": true,
    "sigma": "???",
    "metadata": {},
    "language": "cn",
    "createdAt": "2023-09-07T11:44:23",
    "createdBy": "???",
    "updatedAt": "2023-09-07T11:44:23",
    "updatedBy": "???",
    "visitGroup": null,
    "visitRole": null,
    "visitMode": [
        "r",
        "w",
        "x"
    ],
    "visit": false,
    "directory": false
}

上述数据结构描述了远程存储的文件基本信息,此处对文件阅览器而言,最核心属性如:

属性 含义

name

文件原始名称,下载时也可以使用此名称存储到客户端。

mime / type

文件的MIME,此属性很重要,它描述了远程文件的核心格式,虽然文档阅览器可以通过后缀名对文件类型进行区分,但 mime 是文件类型上传时留下的最本质的元数据,它可以作为严格模式下的文件验证基础,防止用户恶意篡改后缀名导致非法攻击行为。

fileUrl

下载文件专用的地址,内置会调用 Ux.ajaxDownload 的方法来从远程拉取文件的二进制数据内容(也是本小章节需完成的开发内容)。

fileKey

文件唯一键,此唯一键在后端可跨任何系统做全局唯一的标识,从云存储角度打破了所属的维度概念,简单说整个平台上传的文件都只有一个唯一的`fileKey`。

由于 DocViewer 使用的是 class 模式的开发,文件下载流程应该在 componentDidMount 方法中设置,并在预览之前初始化好组件状态。